Skip to content

Commit

Permalink
Initial support for funding-goals-over-time; gratipay#141
Browse files Browse the repository at this point in the history
TODO:
 * Tests
 * Migration of current `goal` field to goals table entry for each user
 * Removing the `goal` column from participant
  • Loading branch information
Christian Wyglendowski committed Mar 18, 2013
1 parent 9175929 commit 7a74b7a
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 7 deletions.
3 changes: 2 additions & 1 deletion gittip/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
from gittip.models.tip import Tip
from gittip.models.transfer import Transfer
from gittip.models.user import User
from gittip.models.goal import Goal

all = [Elsewhere, Exchange, Participant, Payday, Tip, Transfer, User]
all = [Elsewhere, Exchange, Participant, Payday, Tip, Transfer, User]
15 changes: 15 additions & 0 deletions gittip/models/goal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Integer, Numeric, Text, TIMESTAMP

from gittip.orm import db

class Goal(db.Model):
__tablename__ = 'goals'

id = Column(Integer, nullable=False, primary_key=True)
ctime = Column(TIMESTAMP(timezone=True), nullable=False)
mtime = Column(TIMESTAMP(timezone=True), nullable=False, default="now()")
participant = Column(Text, ForeignKey("participants.id", onupdate="CASCADE",

This comment has been minimized.

Copy link
@chadwhitacre

chadwhitacre Mar 19, 2013

I'm seeing onupdate="CASCADE" here, but not the equivalent SQL below.

ondelete="RESTRICT"), nullable=False)
amount = Column(Numeric(precision=35, scale=2), nullable=False)

14 changes: 13 additions & 1 deletion gittip/models/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from sqlalchemy.orm import relationship
from sqlalchemy.schema import Column, CheckConstraint, UniqueConstraint
from sqlalchemy.types import Text, TIMESTAMP, Boolean, Numeric
from sqlalchemy.sql.expression import desc

import gittip
from gittip.models.tip import Tip
Expand Down Expand Up @@ -45,7 +46,6 @@ class Participant(db.Model):
default=0.0, nullable=False)
pending = Column(Numeric(precision=35, scale=2), default=None)
anonymous = Column(Boolean, default=False, nullable=False)
goal = Column(Numeric(precision=35, scale=2), default=None)
balanced_account_uri = Column(Text)
last_ach_result = Column(Text)
is_suspicious = Column(Boolean)
Expand Down Expand Up @@ -80,6 +80,12 @@ class Participant(db.Model):
, foreign_keys="Transfer.tippee"
)

_goal = relationship( "Goal"
, backref="goal"
, foreign_keys="Goal.participant"
, lazy="dynamic"
)

def __eq__(self, other):
return self.id == other.id

Expand Down Expand Up @@ -112,6 +118,12 @@ def valid_tips_receiving(self):
.filter( 'participants.is_suspicious IS NOT true'
, Participant.last_bill_result == ''
)
@property
def goal(self):
query = self._goal.distinct("goals.participant")\
.order_by("goals.participant, goals.mtime DESC")
res = query.first()
return res.amount if res is not None else None

def resolve_unclaimed(self):
if self.accounts_elsewhere:
Expand Down
12 changes: 12 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,15 @@ ALTER TABLE exchanges ADD COLUMN recorder text DEFAULT NULL
ON UPDATE CASCADE ON DELETE RESTRICT;

ALTER TABLE exchanges ADD COLUMN note text DEFAULT NULL;

-------------------------------------------------------------------------------
-- https://github.com/gittip/www.gittip.com/issues/141

-- goals -- all goals a participant has stated over time
CREATE TABLE goals
( id serial PRIMARY KEY
, ctime timestamp with time zone NOT NULL
, mtime timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP
, participant text NOT NULL REFERENCES participants ON DELETE RESTRICT

This comment has been minimized.

Copy link
@chadwhitacre

chadwhitacre Mar 19, 2013

How about ON UPDATE CASCADE?

, amount numeric(35,2) NOT NULL
);
24 changes: 19 additions & 5 deletions www/%participant_id/goal.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,25 @@ if goal is not None:
except decimal.InvalidOperation:
raise Response(400, "Bad input.")

rec = db.fetchone( "UPDATE participants SET goal=%s "
"WHERE id=%s RETURNING goal"
, (goal, user.id)
GOAL = """\

INSERT INTO goals
(ctime, participant, amount)
VALUES ( COALESCE (( SELECT ctime
FROM goals
WHERE (participant=%s)
LIMIT 1
), CURRENT_TIMESTAMP)
, %s, %s
)
RETURNING amount

"""
rec = db.fetchone( GOAL
, (user.id, user.id, goal)
)
goal = rec['goal']

goal = rec['amount']
if goal is not None:
goal = locale.format("%.2f", rec['goal'], grouping=True)
goal = locale.format("%.2f", rec['amount'], grouping=True)
response.body = {"goal": goal}

0 comments on commit 7a74b7a

Please sign in to comment.