Commit ee5f9dde authored by Jeremy Pallats's avatar Jeremy Pallats 💬
Browse files

Schema now using hybrids for queries.

- Update queries that can benefit from direct expression of filters.
- Update properties for systems to follow is_x for descriptive states.
- Systems with x or left merits now expressable as a query.
parent e123395d
Loading
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -816,10 +816,7 @@ class Fort(Action):
    def find_missing(self, left):
        """ Show systems with 'left' remaining. """
        lines = ['__Systems Missing {} Supplies__'.format(left)]

        for system in cogdb.query.fort_get_systems(self.session):
            if not system.is_fortified and not system.skip and system.missing <= left:
                lines.append(system.display(miss=True))
        lines += [x.display(miss=True) for x in cogdb.query.fort_get_systems_x_left(self.session, left)]

        return '\n'.join(lines)

+63 −48
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ import cog.util
import cogdb.eddb
from cog.util import substr_match
from cogdb.schema import (DiscordUser, FortSystem, FortPrep, FortDrop, FortUser, FortOrder,
                          UMSystem, UMUser, UMHold, EUMSheet, KOS,
                          EFortType, UMSystem, UMUser, UMHold, EUMSheet, KOS,
                          AdminPerm, ChannelPerm, RolePerm,
                          TrackSystem, TrackSystemCached, TrackByID, OCRTracker, OCRTrigger,
                          OCRPrep, Global, Vote)
@@ -166,7 +166,7 @@ def check_pref_name(session, new_name):
    except sqla_oexc.NoResultFound:
        pass


# TODO: Do this
def next_sheet_row(session, *, cls, start_row):
    """
    Find the next available row to add in the sheet based on entries.
@@ -201,12 +201,12 @@ def fort_get_medium_systems(session):
    """
    Return unfortified systems designated for small/medium ships.
    """
    mediums = session.query(FortSystem).\
        filter(FortSystem.is_medium, FortSystem.skip == 0).\
    return session.query(FortSystem).\
        filter(FortSystem.is_medium,
               sqla.not_(FortSystem.is_skipped),
               sqla.not_(FortSystem.is_fortified),
               sqla.not_(FortSystem.is_deferred)).\
        all()
    unforted = [med for med in mediums if not med.is_fortified
                and not med.is_deferred]
    return unforted


def fort_get_systems(session, *, mediums=True, ignore_skips=True):
@@ -221,10 +221,9 @@ def fort_get_systems(session, *, mediums=True, ignore_skips=True):
    query = session.query(FortSystem).filter(FortSystem.type != 'prep')

    if ignore_skips:
        query = query.filter(sqla.not_(FortSystem.skip))
        query = query.filter(sqla.not_(FortSystem.is_skipped))
    if not mediums:
        med_names = [med.name for med in fort_get_medium_systems(session)]
        query = query.filter(FortSystem.name.notin_(med_names))
        query = query.filter(sqla.not_(FortSystem.is_medium))

    return query.all()

@@ -243,13 +242,15 @@ def fort_find_current_index(session):
    Raises:
        NoMoreTargets - No more targets left OR a serious problem with data.
    """
    for ind, system in enumerate(fort_get_systems(session)):
        if system.is_fortified or system.skip or system.is_deferred:
            continue

        return ind

    raise cog.exc.NoMoreTargets('No more fort targets at this time.')
    try:
        system_id = session.query(FortSystem.id).\
            filter(sqla.not_(FortSystem.is_fortified),
                   sqla.not_(FortSystem.is_skipped),
                   sqla.not_(FortSystem.is_deferred)).\
            first()
        return system_id[0] - 1
    except sqla_oexc.NoResultFound as exc:
        raise cog.exc.NoMoreTargets('No more fort targets at this time.') from exc


def fort_find_system(session, system_name, search_all=True):
@@ -297,7 +298,7 @@ def fort_get_systems_by_state(session):
            states['undermined'].append(system)
        elif system.is_fortified:
            states['fortified'].append(system)
        elif system.skip:
        elif system.is_skipped:
            states['skipped'].append(system)
        elif system.is_deferred:
            states['almost_done'].append(system)
@@ -322,25 +323,42 @@ def fort_get_next_targets(session, *, offset=0, count=4):
        offset: If set, start offset forward from current active fort target. Default 0
        count: Return this many targets. Default 4
    """
    systems = fort_order_get(session)
    start = 0
    if not systems:
        systems = fort_get_systems(session)
        start = fort_find_current_index(session)
    start += offset
    targets = fort_order_get(session)
    if not targets:
        targets = session.query(FortSystem).\
            filter(sqla.not_(FortSystem.is_skipped),
                   sqla.not_(FortSystem.is_priority),
                   sqla.not_(FortSystem.is_fortified),
                   sqla.not_(FortSystem.is_deferred)).\
            limit(count + offset).\
            all()

    targets = []
    for system in systems[start:]:
        if system.is_fortified or system.priority or system.skip or system.is_deferred:
            continue
    return targets[offset:]

        targets.append(system)
        count -= 1

        if count <= 0:
            break
def fort_get_systems_x_left(session, left=None, *, include_preps=False):
    """
    Return all systems that have merits missing and
    less than or equal to left.

    return targets
    Args:
        session: A session to the db.
        left: The amount that should be missing or less. If not passed, defer_missing constant.

    Kwargs:
        include_preps: By default preps not included, allows to override.
    """
    if not left:
        left = cog.util.CONF.defer_missing

    query = session.query(FortSystem).\
        filter(sqla.not_(FortSystem.is_skipped),
               sqla.not_(FortSystem.is_fortified),
               FortSystem.missing <= left)
    if not include_preps:
        query = query.filter(FortSystem.type != EFortType.prep)

    return query.all()


def fort_get_priority_targets(session):
@@ -348,16 +366,14 @@ def fort_get_priority_targets(session):
    Return all deferred targets under deferal amount.
    This will also return any systems that are prioritized.
    """
    deferred, priority = [], []

    for system in fort_get_systems(session):
        if system.is_fortified:
            continue

        if 'priority' in system.notes.lower():
            priority += [system]
        elif system.is_deferred:
            deferred += [system]
    priority = session.query(FortSystem).\
        filter(sqla.not_(FortSystem.is_skipped),
               FortSystem.is_priority).\
        all()
    deferred = session.query(FortSystem).\
        filter(sqla.not_(FortSystem.is_skipped),
               FortSystem.is_deferred).\
        all()

    return priority, deferred

@@ -482,12 +498,11 @@ def um_get_systems(session, exclude_finished=True, *, sheet_src=EUMSheet.main):
        finished: Return just the finished targets.
    """
    systems = session.query(UMSystem).\
        filter(UMSystem.sheet_src == sheet_src).\
        all()
        filter(UMSystem.sheet_src == sheet_src)
    if exclude_finished:
        systems = [system for system in systems if not system.is_undermined]
        systems = systems.filter(sqla.not_(UMSystem.is_undermined))

    return systems
    return systems.all()


def um_reset_held(session, user, *, sheet_src=EUMSheet.main):
@@ -514,7 +529,7 @@ def um_redeem_merits(session, user, *, sheet_src=EUMSheet.main):
        all()
    for hold in holds:
        total += hold.held
        hold.redeemed = hold.redeemed + hold.held
        hold.redeemed += hold.held
        hold.held = 0

    session.commit()
+87 −46
Original line number Diff line number Diff line
@@ -94,11 +94,16 @@ class DiscordUser(Base):
    def __hash__(self):
        return hash(self.id)

    @property
    @hybrid_property
    def mention(self):
        """ Mention this user in a response. """
        return "<@{}>".format(self.id)

    @mention.expression
    def mention(cls):
        """ Mention this user in a response. """
        return sqla.func.concat("<@", sqla.func.cast(cls.id, sqla.String), ">")

    @property
    def total_merits(self):
        """ The total merits a user has done this cycle. """
@@ -254,41 +259,67 @@ class FortSystem(Base):
        return total

    @cmdr_merits.expression
    def cmdr_merits(self):
    def cmdr_merits(cls):
        """ Total merits dropped by cmdrs """
        return sqla.select([sqla.func.sum(FortDrop.amount)]).\
            where(FortDrop.system_id == self.id).\
            label('cmdr_merits')
        return sqla.func.cast(
            sqla.func.ifnull(
                sqla.select(sqla.func.sum(FortDrop.amount)).
                    where(FortDrop.system_id == cls.id).
                    label('cmdr_merits'),
                0
            ),
            sqla.Integer
        )

    @property
    @hybrid_property
    def ump(self):
        """ Return the undermine percentage, stored as decimal. """
        return '{:.1f}'.format(self.undermine * 100)

    @property
    @ump.expression
    def ump(cls):
        """ Return the undermine percentage, stored as decimal. """
        return sqla.func.round(cls.undermine * 100, 1)

    @hybrid_property
    def current_status(self):
        """ Simply return max fort status reported. """
        return max(self.fort_status, self.cmdr_merits)

    @current_status.expression
    def current_status(cls):
        """ Simply return max fort status reported. """
        return sqla.func.greatest(cls.fort_status, cls.cmdr_merits)

    @hybrid_property
    def priority(self):
    def missing(self):
        """ The remaining supplies to fortify. """
        return max(0, self.trigger - self.current_status)

    @missing.expression
    def missing(cls):
        """ The remaining supplies to fortify. """
        return sqla.func.greatest(0, cls.trigger - cls.current_status)

    @hybrid_property
    def is_priority(self):
        """ The system should be priority. """
        notes = self.notes.lower()
        return 'priority' in notes

    @priority.expression
    def priority(cls):
    @is_priority.expression
    def is_priority(cls):
        """ The system should be priority. """
        return cls.notes.ilike("%priority%")

    @hybrid_property
    def skip(self):
    def is_skipped(self):
        """ The system should be skipped. """
        notes = self.notes.lower()
        return 'leave' in notes or 'skip' in notes

    @skip.expression
    def skip(cls):
    @is_skipped.expression
    def is_skipped(cls):
        """ The system should be skipped. """
        return or_(cls.notes.ilike("%leave%"), cls.notes.ilike("%skip%"))

@@ -302,29 +333,34 @@ class FortSystem(Base):
        """ The system should be skipped. """
        return cls.notes.ilike("%s/m%")

    @property
    @hybrid_property
    def is_fortified(self):
        """ The remaining supplies to fortify """
        """ Check if the system is fortified. """
        return self.fort_override >= 1.0 or self.current_status >= self.trigger

    @is_fortified.expression
    def is_fortified(cls):
        """ Check if the system is fortified. Expression. """
        return sqla.or_(cls.fort_override >= 1.0, cls.current_status >= cls.trigger)

    @hybrid_property
    def is_undermined(self):
        """ The system has been undermined """
        """ Check if the system is undermined. """
        return self.undermine >= 1.00

    @property
    @hybrid_property
    def is_deferred(self):
        """ The system should be deferred. """
        """ Check if the system is deferred. """
        return self.missing > 0 and self.missing <= cog.util.CONF.constants.defer_missing

    @property
    def missing(self):
        """ The remaining supplies to fortify """
        return max(0, self.trigger - self.current_status)
    @is_deferred.expression
    def is_deferred(cls):
        """ Check if the system is deferred. """
        return sqla.and_(cls.missing > 0, cls.missing <= cog.util.CONF.constants.defer_missing)

    @property
    def completion(self):
        """ The fort completion percentage """
        """ The fort completion percentage. """
        try:
            comp_cent = self.current_status / self.trigger * 100
        except ZeroDivisionError:
@@ -417,7 +453,7 @@ class FortPrep(FortSystem):
        primaryjoin='foreign(FortSystem.name) == remote(OCRPrep.system)',
    )

    @property
    @hybrid_property
    def is_fortified(self):
        """ Prep systems never get finished. """
        return False
@@ -545,10 +581,10 @@ class UMUser(Base):
        return total

    @held.expression
    def held(self):
    def held(cls):
        """ Total merits held by this cmdr. """
        return sqla.select([sqla.func.sum(UMHold.held)]).\
            where(sqla.and_(UMHold.user_id == self.id,
            where(sqla.and_(UMHold.user_id == cls.id,
                            UMHold.sheet_src != EUMSheet.snipe)).\
            label('held')

@@ -562,10 +598,10 @@ class UMUser(Base):
        return total

    @redeemed.expression
    def redeemed(self):
    def redeemed(cls):
        """ Total merits redeemed by this cmdr. """
        return sqla.select([sqla.func.sum(UMHold.redeemed)]).\
            where(sqla.and_(UMHold.user_id == self.id,
            where(sqla.and_(UMHold.user_id == cls.id,
                            UMHold.sheet_src != EUMSheet.snipe)).\
            label('redeemed')

@@ -574,15 +610,6 @@ class UMUser(Base):
        """ 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]
@@ -693,23 +720,34 @@ class UMSystem(Base):
        return total

    @cmdr_merits.expression
    def cmdr_merits(self):
        """ Total merits dropped by cmdrs """
        return sqla.select([sqla.func.sum(UMHold.held + UMHold.redeemed)]).\
            where(UMHold.system_id == self.id).\
            label('cmdr_merits')
    def cmdr_merits(cls):
        """ Total merits held or redeemd by cmdrs """
        return sqla.func.cast(
            sqla.func.ifnull(
                sqla.select(sqla.func.sum(UMHold.held + UMHold.redeemed)).
                    where(UMHold.system_id == cls.id).
                    label('cmdr_merits'),
                0
            ),
            sqla.Integer
        )

    @property
    @hybrid_property
    def missing(self):
        """ The remaining supplies to fortify """
        """ The remaining merites targetted to undermine. """
        return self.goal - max(self.cmdr_merits + self.map_offset, self.progress_us)

    @missing.expression
    def missing(cls):
        """ The remaining merites targetted to undermine. """
        return cls.goal - sqla.func.greatest(cls.cmdr_merits + cls.map_offset, cls.progress_us)

    @property
    def descriptor(self):
        """ Descriptive prefix for string. """
        return str(self.type).split('.')[-1].capitalize()

    @property
    @hybrid_property
    def is_undermined(self):
        """
        Return true only if the system is undermined.
@@ -759,7 +797,7 @@ class UMExpand(UMSystem):
        'polymorphic_identity': EUMType.expand,
    }

    @property
    @hybrid_property
    def is_undermined(self):
        """
        Expansions are never finished until tick.
@@ -1774,6 +1812,9 @@ def run_schema_queries(session): # pragma: no cover
    sys = session.query(FortPrep).one()
    print(sys.ocr_prep)

    res = session.query(FortSystem.name, FortSystem.cmdr_merits).filter(FortSystem.cmdr_merits > 1000).all()
    print(res)


if cogdb.TEST_DB:
    recreate_tables()
+17 −0
Original line number Diff line number Diff line
@@ -199,6 +199,23 @@ def test_fort_get_next_targets(session, f_dusers, f_fort_testbed):
    assert [sys.name for sys in targets] == ["LHS 3749", "Alpha Fornacis"]


def test_fort_get_systems_x_left(session, f_dusers, f_fort_testbed):
    systems = cogdb.query.fort_get_systems_x_left(session, 5000)
    assert systems[0].name == 'Nurundere'
    assert len(systems) == 3

    systems = cogdb.query.fort_get_systems_x_left(session, 5000, include_preps=True)
    assert len(systems) == 4


def test_fort_get_priority_targets(session, f_dusers, f_fort_testbed):
    priority, deferred = cogdb.query.fort_get_priority_targets(session)
    assert priority[0].name == 'Othime'
    assert len(priority) == 1
    assert deferred[0].name == 'Dongkum'
    assert len(deferred) == 1


def test_fort_add_drop(session, f_dusers, f_fort_testbed, db_cleanup):
    system = session.query(FortSystem).filter(FortSystem.name == 'Sol').one()
    user = f_fort_testbed[0][-1]
+64 −16
Original line number Diff line number Diff line
@@ -150,6 +150,16 @@ def test_duser_total_merits(f_dusers, f_um_testbed, f_fort_testbed):
    assert f_dusers[0].total_merits == 15050


def test_duser_mention(f_dusers):
    duser = f_dusers[0]
    assert duser.mention == "<@1>"


def test_duser_mention_expression(session, f_dusers):
    result = session.query(DiscordUser.id).filter(DiscordUser.mention.ilike("%1>")).scalar()
    assert result == 1


def test_fortuser__eq__(f_dusers, f_fort_testbed):
    f_user = f_fort_testbed[0][0]
    equal = FortUser(id=1, name='User1', row=22, cry='')
@@ -248,14 +258,6 @@ Fort Status | 4910/4910
UM Status   | 0 (0.00%)
Notes       |```"""

    #  system.fort_status = 4000
    #  assert system.display_details() == """**Frey**
#  ```Completion  | 81.5% (910 left)
#  CMDR Merits | 3700/4910
#  Fort Status | 4000/4910
#  UM Status   | 0 (0.00%)
#  Notes       |```"""


def test_fortsystem_set_status(f_dusers, f_fort_testbed):
    system = f_fort_testbed[1][0]
@@ -276,30 +278,39 @@ def test_fortsystem_current_status(f_dusers, f_fort_testbed):
    assert system.current_status == 4910


def test_fortsystem_current_status_expression(session, f_dusers, f_fort_testbed):
    systems = session.query(FortSystem).\
        filter(FortSystem.current_status >= 4000).\
        order_by(FortSystem.id).\
        all()
    assert len(systems) == 4
    assert systems[0].current_status == 4910


def test_fortsystem_priority(f_dusers, f_fort_testbed):
    system = f_fort_testbed[1][0]
    assert system.priority is False
    assert system.is_priority is False

    system.notes = 'Priority for small ships'
    assert system.priority is True
    assert system.is_priority is True


def test_fortsystem_priority_expression(session, f_dusers, f_fort_testbed):
    skips = session.query(FortSystem.name).filter(FortSystem.priority).all()
    priorities = session.query(FortSystem.name).filter(FortSystem.is_priority).all()

    assert [x[0] for x in skips] == ["Othime"]
    assert [x[0] for x in priorities] == ["Othime"]


def test_fortsystem_skip(f_dusers, f_fort_testbed):
    system = f_fort_testbed[1][0]
    assert system.skip is False
    assert system.is_skipped is False

    system.notes = 'Leave for now.'
    assert system.skip is True
    assert system.is_skipped is True


def test_fortsystem_skip_expression(session, f_dusers, f_fort_testbed):
    skips = session.query(FortSystem.name).filter(FortSystem.skip).all()
    skips = session.query(FortSystem.name).filter(FortSystem.is_skipped).all()

    assert [x[0] for x in skips] == ["Sol", "Phra Mool"]

@@ -327,6 +338,12 @@ def test_fortsystem_is_fortified(f_dusers, f_fort_testbed):
    assert system.is_fortified is False


def test_fortsystem_is_fortified_expression(session, f_dusers, f_fort_testbed):
    sys = session.query(FortSystem).filter(FortSystem.is_fortified).first()
    assert sys.name == 'Frey'
    assert sys.is_fortified


def test_fortsystem_is_undermined(f_dusers, f_fort_testbed):
    system = f_fort_testbed[1][0]

@@ -339,10 +356,19 @@ def test_fortsystem_is_undermined(f_dusers, f_fort_testbed):

def test_fortsystem_is_undermined_expression(session, f_dusers, f_fort_testbed):
    umed = session.query(FortSystem.name).filter(FortSystem.is_undermined).all()

    assert [x[0] for x in umed] == ["WW Piscis Austrini", "LPM 229"]


def test_fortsystem_is_deferred(session, f_dusers, f_fort_testbed):
    system = session.query(FortSystem).filter(FortSystem.name == 'Dongkum').one()
    assert system.is_deferred


def test_fortsystem_is_deferred_expression(session, f_dusers, f_fort_testbed):
    deferred = session.query(FortSystem.name).filter(FortSystem.is_deferred).all()
    assert deferred[0][0] == 'Dongkum'


def test_fortsystem_missing(f_dusers, f_fort_testbed):
    system = f_fort_testbed[1][0]

@@ -356,6 +382,14 @@ def test_fortsystem_missing(f_dusers, f_fort_testbed):
    assert system.missing == 0


def test_fortsystem_missing_expression(session, f_dusers, f_fort_testbed):
    result = session.query(FortSystem.name, FortSystem.missing).\
        filter(FortSystem.missing > 1000).\
        first()
    assert result[0] == 'Nurundere'
    assert result[1] == 3003


def test_fortsystem_completion(f_dusers, f_fort_testbed):
    system = f_fort_testbed[1][0]
    assert system.completion == '100.0'
@@ -521,6 +555,13 @@ def test_umsystem_missing(f_dusers, f_um_testbed):
    assert system.missing == -122


def test_umsystem_missing_expression(session, f_dusers, f_um_testbed):
    systems = session.query(UMSystem.name, UMSystem.missing).\
        filter(UMSystem.missing == 10000).\
        all()
    assert systems == [('Empty', 10000)]


def test_umsystem_is_undermined(session, f_dusers, f_um_testbed):
    control = f_um_testbed[1][1]
    assert not control.is_undermined
@@ -531,6 +572,13 @@ def test_umsystem_is_undermined(session, f_dusers, f_um_testbed):
    assert not exp.is_undermined


def test_umsystem_is_undermined_expression(session, f_dusers, f_um_testbed):
    systems = session.query(UMSystem.name, UMSystem.is_undermined).\
        filter(UMSystem.is_undermined).\
        all()
    assert systems == [('Cemplangpa', True)]


def test_umsystem_set_status(f_dusers, f_um_testbed):
    system = f_um_testbed[1][0]