Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert available funds / contributions/expenditures from area to step charts #216

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
15 changes: 0 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,6 @@ repos:
files: .*/templates/.*\.html$
args: [--tabwidth=2] # tabs should be 2 spaces in Django templates

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.5.1
hooks:
- id: prettier
files: \.(js|ts|jsx|tsx|css|less|json|markdown|md|yaml|yml)$

- repo: https://github.com/pre-commit/mirrors-eslint
rev: "1f7d592"
hooks:
- id: eslint
additional_dependencies:
- [email protected]
- [email protected]
- [email protected]

- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
Expand Down
126 changes: 27 additions & 99 deletions camp_fin/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from collections import namedtuple
from datetime import datetime

from dateutil.rrule import MONTHLY, rrule
from django.db import connection, models
from django.utils import timezone
from django.utils.translation import gettext as _
Expand Down Expand Up @@ -944,52 +943,6 @@ def trends(self, since="2010"):
Generate a dict of filing trends for use in contribution/expenditure charts
for this Entity.
"""

def stack_trends(trend):
"""
Private helper method for compiling trends.
"""
stacked_trend = []
for begin, end, rate in trend:
if not stacked_trend:
stacked_trend.append((rate, begin))
stacked_trend.append((rate, end))

elif begin == stacked_trend[-1][1]:
stacked_trend.append((rate, begin))
stacked_trend.append((rate, end))

elif begin > stacked_trend[-1][1]:
previous_rate, previous_end = stacked_trend[-1]
stacked_trend.append((previous_rate, begin))
stacked_trend.append((rate, begin))
stacked_trend.append((rate, end))

elif begin < stacked_trend[-1][1]:
previous_rate, previous_end = stacked_trend.pop()
stacked_trend.append((previous_rate, begin))
stacked_trend.append((rate + previous_rate, begin))

if end < previous_end:
stacked_trend.append((rate + previous_rate, end))
stacked_trend.append((previous_rate, end))
stacked_trend.append((previous_rate, previous_end))

elif end > previous_end:
stacked_trend.append((rate + previous_rate, previous_end))
stacked_trend.append((rate, previous_end))
stacked_trend.append((rate, end))
else:
stacked_trend.append((rate + previous_rate, end))

flattened_trend = []

for i, point in enumerate(stacked_trend):
rate, date = point
flattened_trend.append([rate, *date])

return flattened_trend

# Balances and debts
summed_filings = """
SELECT
Expand Down Expand Up @@ -1065,68 +1018,43 @@ def stack_trends(trend):
# Donations and expenditures
monthly_query = """
SELECT
{table}.amount AS amount,
{table}.month AS month
FROM {table}_by_month AS {table}
WHERE {table}.entity_id = %s
AND {table}.month >= '{year}-01-01'::date
ORDER BY month
months.year,
months.month,
{table}.amount
FROM (
SELECT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional suggestion: this looks like a good place to use a CTE.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CTEs aren't supported in the version of Postgres we're using for local development (which might not be a constraint in deployment, actually, but don't feel like upgrading Postgres here).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, we should have the development env match up, so I can open an issue for that. fine not to change that here.

DISTINCT DATE_PART('year', month) AS year,
GENERATE_SERIES(1, 12) AS month
FROM {table}_by_month
ORDER BY year, month
) months
LEFT JOIN (
SELECT
{table}.amount AS amount,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about a COALESCE(table.amount, 0) as amount here, and then you can simplify some of the following python code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional suggestion, tbc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added up above!

DATE_PART('month', {table}.month) AS month,
DATE_PART('year', {table}.month) AS year
FROM {table}_by_month AS {table}
WHERE {table}.entity_id = %s
AND {table}.month >= '{year}-01-01'::date
) {table}
USING (year, month)
"""

contributions_query = monthly_query.format(table="contributions", year=since)
expenditures_query = monthly_query.format(table="expenditures", year=since)

cursor.execute(contributions_query, [self.id])

columns = [c[0] for c in cursor.description]
amount_tuple = namedtuple("Amount", columns)
donation_trend = [
[amount or 0, year, month, 1] for year, month, amount in cursor
]

contributions = [amount_tuple(*r) for r in cursor]
expenditures_query = monthly_query.format(table="expenditures", year=since)

cursor.execute(expenditures_query, [self.id])

columns = [c[0] for c in cursor.description]
amount_tuple = namedtuple("Amount", columns)

expenditures = [amount_tuple(*r) for r in cursor]

donation_trend, expend_trend = [], []

if contributions or expenditures:
contributions_lookup = {r.month.date(): r.amount for r in contributions}
expenditures_lookup = {r.month.date(): r.amount for r in expenditures}

start_month = datetime(int(since), 1, 1)

end_month = (
self.filing_set.order_by("-filed_date").first().filed_date.date()
)

for month in rrule(freq=MONTHLY, dtstart=start_month, until=end_month):
replacements = {"month": month.month - 1}

if replacements["month"] < 1:
replacements["month"] = 12
replacements["year"] = month.year - 1

begin_date = month.replace(**replacements)

begin_date_array = [begin_date.year, begin_date.month, begin_date.day]

end_date_array = [month.year, month.month, month.day]

contribution_amount = contributions_lookup.get(month.date(), 0)
expenditure_amount = expenditures_lookup.get(month.date(), 0)

donation_trend.append(
[begin_date_array, end_date_array, contribution_amount]
)
expend_trend.append(
[begin_date_array, end_date_array, (-1 * expenditure_amount)]
)

donation_trend = stack_trends(donation_trend)
expend_trend = stack_trends(expend_trend)
expend_trend = [
[(amount or 0) * -1, year, month, 1] for year, month, amount in cursor
]

output_trends["donation_trend"] = donation_trend
output_trends["expend_trend"] = expend_trend
Expand Down
10 changes: 4 additions & 6 deletions camp_fin/static/js/chart_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ ChartHelper.donation_expenditure = function(el, title, sourceTxt, yaxisLabel, da
return new Highcharts.Chart({
chart: {
renderTo: el,
type: "area",
type: "line",
marginRight: 10,
marginBottom: 25
},
Expand All @@ -208,10 +208,8 @@ ChartHelper.donation_expenditure = function(el, title, sourceTxt, yaxisLabel, da
title: null
},
plotOptions: {
line: {
animation: false
},
series: {
step: true,
marker: {
fillColor: color,
radius: 0,
Expand All @@ -228,13 +226,13 @@ ChartHelper.donation_expenditure = function(el, title, sourceTxt, yaxisLabel, da
tooltip: {
crosshairs: true,
formatter: function() {
var s = "<strong>" + ChartHelper.toolTipDateFormat("day", this.x) + "</strong>";
var s = "<strong>" + ChartHelper.toolTipDateFormat("month", this.x) + "</strong>";
$.each(this.points, function(i, point) {
s += "<br /><span style='color: " + point.series.color + "'>" + point.series.name + ":</span> $" + Highcharts.numberFormat(point.y, 0, '.', ',');
});
return s;
},
shared: true
shared: true,
},
series: seriesData
});
Expand Down
Loading