Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
compute giving and receiving amounts more accurately
Browse files Browse the repository at this point in the history
fixes #1127
  • Loading branch information
Changaco committed Sep 10, 2014
1 parent 88f46f6 commit 23ac5d6
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 75 deletions.
21 changes: 21 additions & 0 deletions branch.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
BEGIN;
DROP VIEW goal_summary;
ALTER TABLE tips ADD COLUMN is_funded boolean;

-- Needs to be recreated to include the new column
DROP VIEW current_tips;
CREATE VIEW current_tips AS
SELECT DISTINCT ON (tipper, tippee) *
FROM tips
ORDER BY tipper, tippee, mtime DESC;

-- Allow updating is_funding via the current_tips view for convenience
CREATE FUNCTION update_tip() RETURNS trigger AS $$
BEGIN
UPDATE tips
SET is_funded = NEW.is_funded
WHERE id = NEW.id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_current_tip INSTEAD OF UPDATE ON current_tips
FOR EACH ROW EXECUTE PROCEDURE update_tip();

\i fake_payday.sql
END;
26 changes: 11 additions & 15 deletions gratipay/billing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import balanced
from aspen.utils import typecheck

from gratipay.models.participant import Participant


def store_result(db, thing, username, new_result):
"""Update the participant's last_{ach,bill}_result in the DB.
Expand Down Expand Up @@ -50,21 +52,15 @@ def store_result(db, thing, username, new_result):
return
if p.is_suspicious or new_result == p.old_result:
return
if new_result == '':
op = '+'
else:
op = '-'
db.run("""
UPDATE participants
SET receiving = (receiving {0} amount)
, npatrons = (npatrons {0} 1)
FROM ( SELECT DISTINCT ON (tippee) tippee, amount
FROM tips
WHERE tipper=%(tipper)s
ORDER BY tippee, mtime DESC
) foo
WHERE tippee = username;
""".format(op), dict(tipper=username))
with db.get_cursor() as cursor:
Participant.from_username(username).update_giving(cursor)
tippees = cursor.all("""
SELECT tippee
FROM current_tips
WHERE tipper=%(tipper)s;
""", dict(tipper=username))
for tippee in tippees:
Participant.from_username(tippee).update_receiving(cursor)


def get_balanced_account(db, username, balanced_customer_href):
Expand Down
22 changes: 5 additions & 17 deletions gratipay/billing/payday.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
from psycopg2 import IntegrityError


with open('fake_payday.sql') as f:
FAKE_PAYDAY = f.read()


def threaded_map(func, iterable, threads=5):
pool = ThreadPool(threads)
r = pool.map(func, iterable)
Expand Down Expand Up @@ -633,24 +637,8 @@ def update_stats(self):


def update_receiving_amounts(self):
UPDATE = """
CREATE OR REPLACE TEMPORARY VIEW total_receiving AS
SELECT tippee, sum(amount) AS amount, count(*) AS ntippers
FROM current_tips
JOIN participants p ON p.username = tipper
WHERE p.is_suspicious IS NOT TRUE
AND p.last_bill_result = ''
AND amount > 0
GROUP BY tippee;
UPDATE participants
SET receiving = (amount + taking)
, npatrons = ntippers
FROM total_receiving
WHERE tippee = username;
"""
with self.db.get_cursor() as cursor:
cursor.execute(UPDATE)
cursor.execute(FAKE_PAYDAY)
log("Updated receiving amounts.")


Expand Down
101 changes: 63 additions & 38 deletions gratipay/models/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from gratipay.models._mixin_team import MixinTeam
from gratipay.models.account_elsewhere import AccountElsewhere
from gratipay.utils.username import safely_reserve_a_username
from gratipay import billing
from gratipay.utils import is_card_expiring


Expand Down Expand Up @@ -581,40 +580,67 @@ def update_is_closed(self, is_closed, cursor=None):
self.set_attributes(is_closed=is_closed)

def update_giving(self, cursor=None):
giving = (cursor or self.db).one("""
# Update is_funded on tips
if self.last_bill_result == '':
(cursor or self.db).run("""
UPDATE current_tips
SET is_funded = true
WHERE tipper = %s
AND is_funded IS NOT true
""", (self.username,))
else:
tips = (cursor or self.db).all("""
SELECT t.*
FROM current_tips t
JOIN participants p2 ON p2.username = t.tippee
WHERE t.tipper = %s
AND t.amount > 0
AND p2.is_suspicious IS NOT true
ORDER BY p2.claimed_time IS NULL, t.ctime ASC
""", (self.username,))
fake_balance = self.balance + self.receiving - self.taking
for tip in tips:
if tip.amount > fake_balance:
is_funded = False
else:
fake_balance -= tip.amount
is_funded = True
if tip.is_funded == is_funded:
continue
(cursor or self.db).run("""
UPDATE tips
SET is_funded = %s
WHERE id = %s
""", (is_funded, tip.id))

# Update giving and pledging on participant
giving, pledging = (cursor or self.db).one("""
WITH our_tips AS (
SELECT amount, tippee, p2.claimed_time
FROM current_tips
JOIN participants p2 ON p2.username = tippee
WHERE tipper = %(username)s
AND p2.is_suspicious IS NOT true
AND amount > 0
AND is_funded
)
UPDATE participants p
SET giving = COALESCE((
SELECT sum(amount)
FROM current_tips
JOIN participants p2 ON p2.username = tippee
WHERE tipper = p.username
AND p2.claimed_time IS NOT NULL
AND p2.is_suspicious IS NOT true
GROUP BY tipper
FROM our_tips
WHERE claimed_time IS NOT NULL
), 0)
WHERE p.username = %s
RETURNING giving
""", (self.username,))
self.set_attributes(giving=giving)

def update_pledging(self, cursor=None):
pledging = (cursor or self.db).one("""
UPDATE participants p
SET pledging = COALESCE((
, pledging = COALESCE((
SELECT sum(amount)
FROM current_tips
JOIN participants p2 ON p2.username = tippee
FROM our_tips
JOIN elsewhere ON elsewhere.participant = tippee
WHERE tipper = p.username
AND p2.claimed_time IS NULL
WHERE claimed_time IS NULL
AND elsewhere.is_locked = false
AND p2.is_suspicious IS NOT true
GROUP BY tipper
), 0)
WHERE p.username = %s
RETURNING pledging
""", (self.username,))
self.set_attributes(pledging=pledging)
WHERE p.username = %(username)s
RETURNING giving, pledging
""", dict(username=self.username))
self.set_attributes(giving=giving, pledging=pledging)

def update_receiving(self, cursor=None):
if self.IS_PLURAL:
Expand All @@ -626,8 +652,8 @@ def update_receiving(self, cursor=None):
JOIN participants p2 ON p2.username = tipper
WHERE tippee = %(username)s
AND p2.is_suspicious IS NOT true
AND p2.last_bill_result = ''
AND amount > 0
AND is_funded
)
UPDATE participants p
SET receiving = (COALESCE((
Expand Down Expand Up @@ -710,10 +736,7 @@ def set_tip_to(self, tippee, amount, update_self=True, update_tippee=True, curso

if update_self:
# Update giving/pledging amount of tipper
if tippee.is_claimed:
self.update_giving(cursor)
else:
self.update_pledging(cursor)
self.update_giving(cursor)
if update_tippee:
# Update receiving amount of tippee
tippee.update_receiving(cursor)
Expand Down Expand Up @@ -771,7 +794,7 @@ def get_tip_distribution(self):
FROM tips
JOIN participants p ON p.username = tipper
WHERE tippee=%s
AND last_bill_result = ''
AND is_funded
AND is_suspicious IS NOT true
ORDER BY tipper
, mtime DESC
Expand Down Expand Up @@ -1060,7 +1083,7 @@ def take_over(self, account, have_confirmation=False):
-- Get all the latest tips from everyone to everyone.
SELECT ctime, tipper, tippee, amount
SELECT ctime, tipper, tippee, amount, is_funded
FROM current_tips
WHERE amount > 0;
Expand All @@ -1073,9 +1096,9 @@ def take_over(self, account, have_confirmation=False):
-- dead and the live account, then we create one new combined tip
-- to the live account (via the GROUP BY and sum()).
INSERT INTO tips (ctime, tipper, tippee, amount)
INSERT INTO tips (ctime, tipper, tippee, amount, is_funded)
SELECT min(ctime), tipper, %(live)s AS tippee, sum(amount)
SELECT min(ctime), tipper, %(live)s AS tippee, sum(amount), bool_and(is_funded)
FROM __temp_unique_tips
Expand Down Expand Up @@ -1312,9 +1335,10 @@ def take_over(self, account, have_confirmation=False):
self.set_attributes(balance=new_balance)

self.update_avatar()
self.update_giving()
self.update_pledging()

# Note: the order matters here, receiving needs to be updated before giving
self.update_receiving()
self.update_giving()

def delete_elsewhere(self, platform, user_id):
"""Deletes account elsewhere unless the user would not be able
Expand Down Expand Up @@ -1347,6 +1371,7 @@ def credit_card_expiring(self, request, response):
return False

try:
from gratipay import billing
card = billing.BalancedCard(self.balanced_customer_href)
year, month = card['expiration_year'], card['expiration_month']
if not (year and month):
Expand Down
6 changes: 3 additions & 3 deletions www/about/stats.spt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ tips_stats = db.one("""
JOIN participants p ON p.username = tipper
JOIN participants p2 ON p2.username = tippee
WHERE amount > 0
AND p.last_bill_result = ''
AND is_funded
AND p2.claimed_time IS NOT NULL
AND p.is_suspicious IS NOT true
AND p2.is_suspicious IS NOT true
Expand All @@ -57,7 +57,7 @@ average_tippees = int(db.one("""\
JOIN participants p ON p.username = tipper
JOIN participants p2 on p2.username = tippee
WHERE amount > 0
AND p.last_bill_result = ''
AND is_funded
AND p2.claimed_time IS NOT NULL
AND p.is_suspicious IS NOT true
AND p2.is_suspicious IS NOT true
Expand All @@ -82,7 +82,7 @@ TIP_DISTRIBUTION = """
JOIN participants p ON p.username = tipper
JOIN participants p2 on p2.username = tippee
WHERE amount > 0
AND p.last_bill_result = ''
AND is_funded
AND p2.claimed_time IS NOT NULL
AND p.is_suspicious IS NOT true
AND p2.is_suspicious IS NOT true
Expand Down
2 changes: 1 addition & 1 deletion www/about/tip-distribution.json.spt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ website.db.all("""
FROM tips
JOIN participants p ON p.username = tipper
JOIN participants p2 on p2.username = tippee
WHERE p.last_bill_result = ''
WHERE is_funded
AND p2.claimed_time IS NOT NULL
AND NOT (p.is_suspicious IS true)
AND NOT (p2.is_suspicious IS true)
Expand Down
1 change: 0 additions & 1 deletion www/for/%slug/index.html.spt
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ givers = query_cache.all("""
WHERE is_suspicious IS NOT true
AND giving > 0
AND cc.is_member
AND last_bill_result = ''
ORDER BY giving DESC
LIMIT %s
OFFSET %s
Expand Down

0 comments on commit 23ac5d6

Please sign in to comment.