Commit 01e349ea authored by Jeremy Pallats's avatar Jeremy Pallats 💬
Browse files

FIX #120: Add Vote Graph/Live Data

- Modify server to provide consolidation information by cycle or after a
  date timestamp.
- Allow users to select what cycle they'd like to see in the chart.
- Ensure new data requested only inside current cycle range.
- Fix the scale height of time axis.
parent 77d1781a
Loading
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ Rolling over existing file logs as listed below.
    module_name -> output_file
    =========================="""
# This date is the first week of Elite's Powerplay cycles
WEEK_ZERO = datetime.datetime(2015, 8, 6, 7, 0)
WEEK_ZERO = datetime.datetime(2015, 5, 28, 7, 0)


class RWLockWrite():
@@ -558,6 +558,13 @@ def current_cycle():
    return (datetime.datetime.utcnow() - WEEK_ZERO).days // 7


def cycle_to_start(cycle_number):
    """
    Returns: The starting datetime of cycle_number.
    """
    return WEEK_ZERO + datetime.timedelta(weeks=cycle_number)


# TODO: Use later.
class TimestampMixin():
    """
+12 −9
Original line number Diff line number Diff line
@@ -2,7 +2,6 @@
Module should handle logic related to querying/manipulating tables from a high level.
"""
import copy
import datetime
import logging
import os
import tempfile
@@ -1548,18 +1547,22 @@ def get_snipe_members_holding(session, guild):
    return reply


def get_consolidation_after(session, a_datetime=None):
def get_consolidation_in_range(session, start, end=None):
    """
    Return all consolidation tracking data since the last tick.

    If start and end left bbank, return this cycle's votes.
    Dates are inclusive on both sides.

    Args:
        session: Session to the db.
        a_datetime: If present, fetch only those votes after this datetime.
        start: Fetch only those entries after this datetime, this date required.
        end: If present, fetch only those entries before this datetime.
    """
    if not a_datetime:
        a_datetime = cog.util.next_weekly_tick(datetime.datetime.utcnow(), -1)
    query = session.query(Consolidation).\
        filter(Consolidation.updated_at >= start)

    return session.query(Consolidation).\
        filter(Consolidation.updated_at > a_datetime).\
        order_by(Consolidation.updated_at.asc()).\
        all()
    if end:
        query = query.filter(Consolidation.updated_at <= end)

    return query.order_by(Consolidation.updated_at.asc()).all()
+5 −1
Original line number Diff line number Diff line
@@ -320,4 +320,8 @@ def test_chunk_file():
@mock.patch('cog.util.datetime')
def test_current_cycle(mock_date):
    mock_date.datetime.utcnow.return_value = datetime.datetime(2021, 10, 10, 2, 43, 22, 828086)
    assert cog.util.current_cycle() == 322
    assert cog.util.current_cycle() == 332


def test_cycle_to_start():
    assert cog.util.cycle_to_start(334) == datetime.datetime(2021, 10, 21, 7, 0)
+10 −2
Original line number Diff line number Diff line
@@ -967,6 +967,14 @@ def test_get_snipe_members_holding(session, f_bot, f_dusers, f_um_testbed):
    assert results == expected


def test_get_consolidation_after(session, f_cons_data):
    values = cogdb.query.get_consolidation_after(session)
def test_get_consolidation_in_range_default(session, f_cons_data):
    after = f_cons_data[0].updated_at - datetime.timedelta(minutes=5)
    values = cogdb.query.get_consolidation_in_range(session, start=after)
    assert [x.amount for x in values] == [66, 67, 65, 64, 67, 68]


def test_get_consolidation_in_range_both(session, f_cons_data):
    after = f_cons_data[1]
    before = f_cons_data[-3]
    values = cogdb.query.get_consolidation_in_range(session, start=after.updated_at, end=before.updated_at)
    assert [x.amount for x in values] == [67, 65, 64]
+31 −14
Original line number Diff line number Diff line
@@ -72,17 +72,12 @@ def init_log():
        logger.addHandler(hand)


def get_vote_data(session, epoch=0):
def get_vote_data(session, start=None, end=None):
    """
    Retrieve and format the vote data.
    """
    try:
        epoch = datetime.datetime.utcfromtimestamp(epoch)
    except ValueError:
        epoch = 0

    data = []
    for con in cogdb.query.get_consolidation_after(session, epoch):
    for con in cogdb.query.get_consolidation_in_range(session, start=start, end=end):
        data += [{
            "x": datetime.datetime.strftime(con.updated_at, CHART_FMT),
            "y": con.amount,
@@ -98,20 +93,42 @@ async def vote(request):
    """
    The route to provide vote information via charts.
    """
    cur_cycle = cog.util.current_cycle()
    start = cog.util.cycle_to_start(cur_cycle)
    end = start + datetime.timedelta(weeks=1)
    with cogdb.session_scope(cogdb.Session) as session:
        data = get_vote_data(session)
        data = get_vote_data(session, start, end)

    return sanic.response.html(TEMPLATES['vote'].render(request=request, data=data))
    return sanic.response.html(TEMPLATES['vote'].render(request=request, data=data, cycle=cur_cycle))


@app.route('/data/vote/<epoch:int>', methods=['GET'])
async def vote_data(request, epoch):
@app.route('/data/voteRange/<start:int>/<end:int>', methods=['GET'])
async def vote_range(request, start, end):
    """Provide voting information after a given datetime.

    Args:
        request: The request object for flask.
        start: The timestamp (UTC) to get data after.
        end: The timestamp (UTC) to get data before.
    """
    Provide the data via direct request.
    Response is JSON.
    start = datetime.datetime.utcfromtimestamp(start)
    end = datetime.datetime.utcfromtimestamp(end)
    with cogdb.session_scope(cogdb.Session) as session:
        return sanic.response.json(get_vote_data(session, start, end))


@app.route('/data/voteCycle/<cycle:int>', methods=['GET'])
async def vote_cycle(request, cycle):
    """Provide vote consolidation data for a given cycle.

    Args:
        request: The request object for flask.
        cycle: The cycle to get voting data for.
    """
    start = cog.util.cycle_to_start(cycle)
    end = start + datetime.timedelta(weeks=1)
    with cogdb.session_scope(cogdb.Session) as session:
        return sanic.response.json(get_vote_data(session, epoch))
        return sanic.response.json(get_vote_data(session, start, end))


@app.route('/post', methods=['GET', 'POST'])
Loading