-
Notifications
You must be signed in to change notification settings - Fork 1
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
base: main
Are you sure you want to change the base?
Changes from 4 commits
3da50d3
e5796ec
8c7b59a
26d670d
8a95238
4338d93
99fca26
f21a76e
16dfa3d
a3f98a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: | ||
|
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 _ | ||
|
@@ -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 | ||
|
@@ -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 | ||
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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional suggestion, tbc. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.