We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
add 2.0 payments to history pages
@ehmatthes at https://github.com/gratipay/gratipay.com/issues/3446#issuecomment-103163666:

> I made an account for introtopython.org last week, and it was one of the teams that should have been included in last week's payday. But my balance is low enough that I didn't get a payout.
> 
> How will I know if my accounts are set up correctly, ie if my team was included in last week's payday? I don't know how to view my team account, or if it's even possible to view a profile page for that team yet. introtopython is not included in my teams page. My individual history doesn't show anything after payday 152.
#### Notify:
- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2233
- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2358
- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2405
- [x]  https://gratipay.freshdesk.com/helpdesk/tickets/2514
- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2468
- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2354
- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2540
- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2450


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gratipay/utils/history.py`
Content:
```
1 from datetime import datetime
2 from decimal import Decimal
3 
4 from aspen import Response
5 from psycopg2 import IntegrityError
6 
7 
8 def get_end_of_year_balance(db, participant, year, current_year):
9     if year == current_year:
10         return participant.balance
11     start = participant.claimed_time or participant.ctime
12     if year < start.year:
13         return Decimal('0.00')
14 
15     balance = db.one("""
16         SELECT balance
17           FROM balances_at
18          WHERE participant = %s
19            AND "at" = %s
20     """, (participant.id, datetime(year+1, 1, 1)))
21     if balance is not None:
22         return balance
23 
24     username = participant.username
25     start_balance = get_end_of_year_balance(db, participant, year-1, current_year)
26     delta = db.one("""
27         SELECT (
28                   SELECT COALESCE(sum(amount), 0) AS a
29                     FROM exchanges
30                    WHERE participant = %(username)s
31                      AND extract(year from timestamp) = %(year)s
32                      AND amount > 0
33                      AND (status is null OR status = 'succeeded')
34                ) + (
35                   SELECT COALESCE(sum(amount-fee), 0) AS a
36                     FROM exchanges
37                    WHERE participant = %(username)s
38                      AND extract(year from timestamp) = %(year)s
39                      AND amount < 0
40                      AND (status is null OR status <> 'failed')
41                ) + (
42                   SELECT COALESCE(sum(-amount), 0) AS a
43                     FROM transfers
44                    WHERE tipper = %(username)s
45                      AND extract(year from timestamp) = %(year)s
46                ) + (
47                   SELECT COALESCE(sum(amount), 0) AS a
48                     FROM transfers
49                    WHERE tippee = %(username)s
50                      AND extract(year from timestamp) = %(year)s
51                ) AS delta
52     """, locals())
53     balance = start_balance + delta
54     try:
55         db.run("""
56             INSERT INTO balances_at
57                         (participant, at, balance)
58                  VALUES (%s, %s, %s)
59         """, (participant.id, datetime(year+1, 1, 1), balance))
60     except IntegrityError:
61         pass
62     return balance
63 
64 
65 def iter_payday_events(db, participant, year=None):
66     """Yields payday events for the given participant.
67     """
68     current_year = datetime.utcnow().year
69     year = year or current_year
70 
71     username = participant.username
72     exchanges = db.all("""
73         SELECT *
74           FROM exchanges
75          WHERE participant=%(username)s
76            AND extract(year from timestamp) = %(year)s
77     """, locals(), back_as=dict)
78     transfers = db.all("""
79         SELECT *
80           FROM transfers
81          WHERE (tipper=%(username)s OR tippee=%(username)s)
82            AND extract(year from timestamp) = %(year)s
83     """, locals(), back_as=dict)
84 
85     if not (exchanges or transfers):
86         return
87 
88     if transfers:
89         yield dict(
90             kind='totals',
91             given=sum(t['amount'] for t in transfers if t['tipper'] == username and t['context'] != 'take'),
92             received=sum(t['amount'] for t in transfers if t['tippee'] == username),
93         )
94 
95     payday_dates = db.all("""
96         SELECT ts_start::date
97           FROM paydays
98       ORDER BY ts_start ASC
99     """)
100 
101     balance = get_end_of_year_balance(db, participant, year, current_year)
102     prev_date = None
103     get_timestamp = lambda e: e['timestamp']
104     events = sorted(exchanges+transfers, key=get_timestamp, reverse=True)
105     for event in events:
106 
107         event['balance'] = balance
108 
109         event_date = event['timestamp'].date()
110         if event_date != prev_date:
111             if prev_date:
112                 yield dict(kind='day-close', balance=balance)
113             day_open = dict(kind='day-open', date=event_date, balance=balance)
114             if payday_dates:
115                 while payday_dates and payday_dates[-1] > event_date:
116                     payday_dates.pop()
117                 payday_date = payday_dates[-1] if payday_dates else None
118                 if event_date == payday_date:
119                     day_open['payday_number'] = len(payday_dates) - 1
120             yield day_open
121             prev_date = event_date
122 
123         if 'fee' in event:
124             if event['amount'] > 0:
125                 kind = 'charge'
126                 if event['status'] in (None, 'succeeded'):
127                     balance -= event['amount']
128             else:
129                 kind = 'credit'
130                 if event['status'] != 'failed':
131                     balance -= event['amount'] - event['fee']
132         else:
133             kind = 'transfer'
134             if event['tippee'] == username:
135                 balance -= event['amount']
136             else:
137                 balance += event['amount']
138         event['kind'] = kind
139 
140         yield event
141 
142     yield dict(kind='day-close', balance=balance)
143 
144 
145 def export_history(participant, year, mode, key, back_as='namedtuple', require_key=False):
146     db = participant.db
147     params = dict(username=participant.username, year=year)
148     out = {}
149     if mode == 'aggregate':
150         out['given'] = lambda: db.all("""
151             SELECT tippee, sum(amount) AS amount
152               FROM transfers
153              WHERE tipper = %(username)s
154                AND extract(year from timestamp) = %(year)s
155           GROUP BY tippee
156         """, params, back_as=back_as)
157         out['taken'] = lambda: db.all("""
158             SELECT tipper AS team, sum(amount) AS amount
159               FROM transfers
160              WHERE tippee = %(username)s
161                AND context = 'take'
162                AND extract(year from timestamp) = %(year)s
163           GROUP BY tipper
164         """, params, back_as=back_as)
165     else:
166         out['exchanges'] = lambda: db.all("""
167             SELECT timestamp, amount, fee, status, note
168               FROM exchanges
169              WHERE participant = %(username)s
170                AND extract(year from timestamp) = %(year)s
171           ORDER BY timestamp ASC
172         """, params, back_as=back_as)
173         out['given'] = lambda: db.all("""
174             SELECT timestamp, tippee, amount, context
175               FROM transfers
176              WHERE tipper = %(username)s
177                AND extract(year from timestamp) = %(year)s
178           ORDER BY timestamp ASC
179         """, params, back_as=back_as)
180         out['taken'] = lambda: db.all("""
181             SELECT timestamp, tipper AS team, amount
182               FROM transfers
183              WHERE tippee = %(username)s
184                AND context = 'take'
185                AND extract(year from timestamp) = %(year)s
186           ORDER BY timestamp ASC
187         """, params, back_as=back_as)
188         out['received'] = lambda: db.all("""
189             SELECT timestamp, amount, context
190               FROM transfers
191              WHERE tippee = %(username)s
192                AND context NOT IN ('take', 'take-over')
193                AND extract(year from timestamp) = %(year)s
194           ORDER BY timestamp ASC
195         """, params, back_as=back_as)
196 
197     if key:
198         try:
199             return out[key]()
200         except KeyError:
201             raise Response(400, "bad key `%s`" % key)
202     elif require_key:
203         raise Response(400, "missing `key` parameter")
204     else:
205         return {k: v() for k, v in out.items()}
206
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gratipay/utils/history.py b/gratipay/utils/history.py
--- a/gratipay/utils/history.py
+++ b/gratipay/utils/history.py
@@ -75,6 +75,12 @@
          WHERE participant=%(username)s
            AND extract(year from timestamp) = %(year)s
     """, locals(), back_as=dict)
+    payments = db.all("""
+        SELECT *
+          FROM payments
+         WHERE participant=%(username)s
+           AND extract(year from timestamp) = %(year)s
+    """, locals(), back_as=dict)
     transfers = db.all("""
         SELECT *
           FROM transfers
@@ -82,15 +88,20 @@
            AND extract(year from timestamp) = %(year)s
     """, locals(), back_as=dict)
 
-    if not (exchanges or transfers):
+    if not (exchanges or payments or transfers):
         return
 
-    if transfers:
-        yield dict(
-            kind='totals',
-            given=sum(t['amount'] for t in transfers if t['tipper'] == username and t['context'] != 'take'),
-            received=sum(t['amount'] for t in transfers if t['tippee'] == username),
-        )
+    if payments or transfers:
+        payments_given = sum([p['amount'] for p in payments if p['direction'] == 'to-team'])
+        payments_received = sum([p['amount'] for p in payments \
+                                                            if p['direction'] == 'to-participant'])
+        transfers_given = sum(t['amount'] for t in transfers \
+                                             if t['tipper'] == username and t['context'] != 'take')
+        transfers_received = sum(t['amount'] for t in transfers if t['tippee'] == username)
+        yield dict( kind='totals'
+                  , given=payments_given + transfers_given
+                  , received=payments_received + transfers_received
+                   )
 
     payday_dates = db.all("""
         SELECT ts_start::date
@@ -101,7 +112,7 @@
     balance = get_end_of_year_balance(db, participant, year, current_year)
     prev_date = None
     get_timestamp = lambda e: e['timestamp']
-    events = sorted(exchanges+transfers, key=get_timestamp, reverse=True)
+    events = sorted(exchanges+payments+transfers, key=get_timestamp, reverse=True)
     for event in events:
 
         event['balance'] = balance
@@ -129,6 +140,13 @@
                 kind = 'credit'
                 if event['status'] != 'failed':
                     balance -= event['amount'] - event['fee']
+        elif 'direction' in event:
+            kind = 'payment'
+            if event['direction'] == 'to-participant':
+                balance -= event['amount']
+            else:
+                assert event['direction'] == 'to-team'
+                balance += event['amount']
         else:
             kind = 'transfer'
             if event['tippee'] == username:

{"golden_diff": "diff --git a/gratipay/utils/history.py b/gratipay/utils/history.py\n--- a/gratipay/utils/history.py\n+++ b/gratipay/utils/history.py\n@@ -75,6 +75,12 @@\n          WHERE participant=%(username)s\n            AND extract(year from timestamp) = %(year)s\n     \"\"\", locals(), back_as=dict)\n+    payments = db.all(\"\"\"\n+        SELECT *\n+          FROM payments\n+         WHERE participant=%(username)s\n+           AND extract(year from timestamp) = %(year)s\n+    \"\"\", locals(), back_as=dict)\n     transfers = db.all(\"\"\"\n         SELECT *\n           FROM transfers\n@@ -82,15 +88,20 @@\n            AND extract(year from timestamp) = %(year)s\n     \"\"\", locals(), back_as=dict)\n \n-    if not (exchanges or transfers):\n+    if not (exchanges or payments or transfers):\n         return\n \n-    if transfers:\n-        yield dict(\n-            kind='totals',\n-            given=sum(t['amount'] for t in transfers if t['tipper'] == username and t['context'] != 'take'),\n-            received=sum(t['amount'] for t in transfers if t['tippee'] == username),\n-        )\n+    if payments or transfers:\n+        payments_given = sum([p['amount'] for p in payments if p['direction'] == 'to-team'])\n+        payments_received = sum([p['amount'] for p in payments \\\n+                                                            if p['direction'] == 'to-participant'])\n+        transfers_given = sum(t['amount'] for t in transfers \\\n+                                             if t['tipper'] == username and t['context'] != 'take')\n+        transfers_received = sum(t['amount'] for t in transfers if t['tippee'] == username)\n+        yield dict( kind='totals'\n+                  , given=payments_given + transfers_given\n+                  , received=payments_received + transfers_received\n+                   )\n \n     payday_dates = db.all(\"\"\"\n         SELECT ts_start::date\n@@ -101,7 +112,7 @@\n     balance = get_end_of_year_balance(db, participant, year, current_year)\n     prev_date = None\n     get_timestamp = lambda e: e['timestamp']\n-    events = sorted(exchanges+transfers, key=get_timestamp, reverse=True)\n+    events = sorted(exchanges+payments+transfers, key=get_timestamp, reverse=True)\n     for event in events:\n \n         event['balance'] = balance\n@@ -129,6 +140,13 @@\n                 kind = 'credit'\n                 if event['status'] != 'failed':\n                     balance -= event['amount'] - event['fee']\n+        elif 'direction' in event:\n+            kind = 'payment'\n+            if event['direction'] == 'to-participant':\n+                balance -= event['amount']\n+            else:\n+                assert event['direction'] == 'to-team'\n+                balance += event['amount']\n         else:\n             kind = 'transfer'\n             if event['tippee'] == username:\n", "issue": "add 2.0 payments to history pages\n@ehmatthes at https://github.com/gratipay/gratipay.com/issues/3446#issuecomment-103163666:\n\n> I made an account for introtopython.org last week, and it was one of the teams that should have been included in last week's payday. But my balance is low enough that I didn't get a payout.\n> \n> How will I know if my accounts are set up correctly, ie if my team was included in last week's payday? I don't know how to view my team account, or if it's even possible to view a profile page for that team yet. introtopython is not included in my teams page. My individual history doesn't show anything after payday 152.\n#### Notify:\n- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2233\n- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2358\n- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2405\n- [x]  https://gratipay.freshdesk.com/helpdesk/tickets/2514\n- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2468\n- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2354\n- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2540\n- [x] https://gratipay.freshdesk.com/helpdesk/tickets/2450\n\n", "before_files": [{"content": "from datetime import datetime\nfrom decimal import Decimal\n\nfrom aspen import Response\nfrom psycopg2 import IntegrityError\n\n\ndef get_end_of_year_balance(db, participant, year, current_year):\n    if year == current_year:\n        return participant.balance\n    start = participant.claimed_time or participant.ctime\n    if year < start.year:\n        return Decimal('0.00')\n\n    balance = db.one(\"\"\"\n        SELECT balance\n          FROM balances_at\n         WHERE participant = %s\n           AND \"at\" = %s\n    \"\"\", (participant.id, datetime(year+1, 1, 1)))\n    if balance is not None:\n        return balance\n\n    username = participant.username\n    start_balance = get_end_of_year_balance(db, participant, year-1, current_year)\n    delta = db.one(\"\"\"\n        SELECT (\n                  SELECT COALESCE(sum(amount), 0) AS a\n                    FROM exchanges\n                   WHERE participant = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n                     AND amount > 0\n                     AND (status is null OR status = 'succeeded')\n               ) + (\n                  SELECT COALESCE(sum(amount-fee), 0) AS a\n                    FROM exchanges\n                   WHERE participant = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n                     AND amount < 0\n                     AND (status is null OR status <> 'failed')\n               ) + (\n                  SELECT COALESCE(sum(-amount), 0) AS a\n                    FROM transfers\n                   WHERE tipper = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n               ) + (\n                  SELECT COALESCE(sum(amount), 0) AS a\n                    FROM transfers\n                   WHERE tippee = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n               ) AS delta\n    \"\"\", locals())\n    balance = start_balance + delta\n    try:\n        db.run(\"\"\"\n            INSERT INTO balances_at\n                        (participant, at, balance)\n                 VALUES (%s, %s, %s)\n        \"\"\", (participant.id, datetime(year+1, 1, 1), balance))\n    except IntegrityError:\n        pass\n    return balance\n\n\ndef iter_payday_events(db, participant, year=None):\n    \"\"\"Yields payday events for the given participant.\n    \"\"\"\n    current_year = datetime.utcnow().year\n    year = year or current_year\n\n    username = participant.username\n    exchanges = db.all(\"\"\"\n        SELECT *\n          FROM exchanges\n         WHERE participant=%(username)s\n           AND extract(year from timestamp) = %(year)s\n    \"\"\", locals(), back_as=dict)\n    transfers = db.all(\"\"\"\n        SELECT *\n          FROM transfers\n         WHERE (tipper=%(username)s OR tippee=%(username)s)\n           AND extract(year from timestamp) = %(year)s\n    \"\"\", locals(), back_as=dict)\n\n    if not (exchanges or transfers):\n        return\n\n    if transfers:\n        yield dict(\n            kind='totals',\n            given=sum(t['amount'] for t in transfers if t['tipper'] == username and t['context'] != 'take'),\n            received=sum(t['amount'] for t in transfers if t['tippee'] == username),\n        )\n\n    payday_dates = db.all(\"\"\"\n        SELECT ts_start::date\n          FROM paydays\n      ORDER BY ts_start ASC\n    \"\"\")\n\n    balance = get_end_of_year_balance(db, participant, year, current_year)\n    prev_date = None\n    get_timestamp = lambda e: e['timestamp']\n    events = sorted(exchanges+transfers, key=get_timestamp, reverse=True)\n    for event in events:\n\n        event['balance'] = balance\n\n        event_date = event['timestamp'].date()\n        if event_date != prev_date:\n            if prev_date:\n                yield dict(kind='day-close', balance=balance)\n            day_open = dict(kind='day-open', date=event_date, balance=balance)\n            if payday_dates:\n                while payday_dates and payday_dates[-1] > event_date:\n                    payday_dates.pop()\n                payday_date = payday_dates[-1] if payday_dates else None\n                if event_date == payday_date:\n                    day_open['payday_number'] = len(payday_dates) - 1\n            yield day_open\n            prev_date = event_date\n\n        if 'fee' in event:\n            if event['amount'] > 0:\n                kind = 'charge'\n                if event['status'] in (None, 'succeeded'):\n                    balance -= event['amount']\n            else:\n                kind = 'credit'\n                if event['status'] != 'failed':\n                    balance -= event['amount'] - event['fee']\n        else:\n            kind = 'transfer'\n            if event['tippee'] == username:\n                balance -= event['amount']\n            else:\n                balance += event['amount']\n        event['kind'] = kind\n\n        yield event\n\n    yield dict(kind='day-close', balance=balance)\n\n\ndef export_history(participant, year, mode, key, back_as='namedtuple', require_key=False):\n    db = participant.db\n    params = dict(username=participant.username, year=year)\n    out = {}\n    if mode == 'aggregate':\n        out['given'] = lambda: db.all(\"\"\"\n            SELECT tippee, sum(amount) AS amount\n              FROM transfers\n             WHERE tipper = %(username)s\n               AND extract(year from timestamp) = %(year)s\n          GROUP BY tippee\n        \"\"\", params, back_as=back_as)\n        out['taken'] = lambda: db.all(\"\"\"\n            SELECT tipper AS team, sum(amount) AS amount\n              FROM transfers\n             WHERE tippee = %(username)s\n               AND context = 'take'\n               AND extract(year from timestamp) = %(year)s\n          GROUP BY tipper\n        \"\"\", params, back_as=back_as)\n    else:\n        out['exchanges'] = lambda: db.all(\"\"\"\n            SELECT timestamp, amount, fee, status, note\n              FROM exchanges\n             WHERE participant = %(username)s\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n        out['given'] = lambda: db.all(\"\"\"\n            SELECT timestamp, tippee, amount, context\n              FROM transfers\n             WHERE tipper = %(username)s\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n        out['taken'] = lambda: db.all(\"\"\"\n            SELECT timestamp, tipper AS team, amount\n              FROM transfers\n             WHERE tippee = %(username)s\n               AND context = 'take'\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n        out['received'] = lambda: db.all(\"\"\"\n            SELECT timestamp, amount, context\n              FROM transfers\n             WHERE tippee = %(username)s\n               AND context NOT IN ('take', 'take-over')\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n\n    if key:\n        try:\n            return out[key]()\n        except KeyError:\n            raise Response(400, \"bad key `%s`\" % key)\n    elif require_key:\n        raise Response(400, \"missing `key` parameter\")\n    else:\n        return {k: v() for k, v in out.items()}\n", "path": "gratipay/utils/history.py"}], "after_files": [{"content": "from datetime import datetime\nfrom decimal import Decimal\n\nfrom aspen import Response\nfrom psycopg2 import IntegrityError\n\n\ndef get_end_of_year_balance(db, participant, year, current_year):\n    if year == current_year:\n        return participant.balance\n    start = participant.claimed_time or participant.ctime\n    if year < start.year:\n        return Decimal('0.00')\n\n    balance = db.one(\"\"\"\n        SELECT balance\n          FROM balances_at\n         WHERE participant = %s\n           AND \"at\" = %s\n    \"\"\", (participant.id, datetime(year+1, 1, 1)))\n    if balance is not None:\n        return balance\n\n    username = participant.username\n    start_balance = get_end_of_year_balance(db, participant, year-1, current_year)\n    delta = db.one(\"\"\"\n        SELECT (\n                  SELECT COALESCE(sum(amount), 0) AS a\n                    FROM exchanges\n                   WHERE participant = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n                     AND amount > 0\n                     AND (status is null OR status = 'succeeded')\n               ) + (\n                  SELECT COALESCE(sum(amount-fee), 0) AS a\n                    FROM exchanges\n                   WHERE participant = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n                     AND amount < 0\n                     AND (status is null OR status <> 'failed')\n               ) + (\n                  SELECT COALESCE(sum(-amount), 0) AS a\n                    FROM transfers\n                   WHERE tipper = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n               ) + (\n                  SELECT COALESCE(sum(amount), 0) AS a\n                    FROM transfers\n                   WHERE tippee = %(username)s\n                     AND extract(year from timestamp) = %(year)s\n               ) AS delta\n    \"\"\", locals())\n    balance = start_balance + delta\n    try:\n        db.run(\"\"\"\n            INSERT INTO balances_at\n                        (participant, at, balance)\n                 VALUES (%s, %s, %s)\n        \"\"\", (participant.id, datetime(year+1, 1, 1), balance))\n    except IntegrityError:\n        pass\n    return balance\n\n\ndef iter_payday_events(db, participant, year=None):\n    \"\"\"Yields payday events for the given participant.\n    \"\"\"\n    current_year = datetime.utcnow().year\n    year = year or current_year\n\n    username = participant.username\n    exchanges = db.all(\"\"\"\n        SELECT *\n          FROM exchanges\n         WHERE participant=%(username)s\n           AND extract(year from timestamp) = %(year)s\n    \"\"\", locals(), back_as=dict)\n    payments = db.all(\"\"\"\n        SELECT *\n          FROM payments\n         WHERE participant=%(username)s\n           AND extract(year from timestamp) = %(year)s\n    \"\"\", locals(), back_as=dict)\n    transfers = db.all(\"\"\"\n        SELECT *\n          FROM transfers\n         WHERE (tipper=%(username)s OR tippee=%(username)s)\n           AND extract(year from timestamp) = %(year)s\n    \"\"\", locals(), back_as=dict)\n\n    if not (exchanges or payments or transfers):\n        return\n\n    if payments or transfers:\n        payments_given = sum([p['amount'] for p in payments if p['direction'] == 'to-team'])\n        payments_received = sum([p['amount'] for p in payments \\\n                                                            if p['direction'] == 'to-participant'])\n        transfers_given = sum(t['amount'] for t in transfers \\\n                                             if t['tipper'] == username and t['context'] != 'take')\n        transfers_received = sum(t['amount'] for t in transfers if t['tippee'] == username)\n        yield dict( kind='totals'\n                  , given=payments_given + transfers_given\n                  , received=payments_received + transfers_received\n                   )\n\n    payday_dates = db.all(\"\"\"\n        SELECT ts_start::date\n          FROM paydays\n      ORDER BY ts_start ASC\n    \"\"\")\n\n    balance = get_end_of_year_balance(db, participant, year, current_year)\n    prev_date = None\n    get_timestamp = lambda e: e['timestamp']\n    events = sorted(exchanges+payments+transfers, key=get_timestamp, reverse=True)\n    for event in events:\n\n        event['balance'] = balance\n\n        event_date = event['timestamp'].date()\n        if event_date != prev_date:\n            if prev_date:\n                yield dict(kind='day-close', balance=balance)\n            day_open = dict(kind='day-open', date=event_date, balance=balance)\n            if payday_dates:\n                while payday_dates and payday_dates[-1] > event_date:\n                    payday_dates.pop()\n                payday_date = payday_dates[-1] if payday_dates else None\n                if event_date == payday_date:\n                    day_open['payday_number'] = len(payday_dates) - 1\n            yield day_open\n            prev_date = event_date\n\n        if 'fee' in event:\n            if event['amount'] > 0:\n                kind = 'charge'\n                if event['status'] in (None, 'succeeded'):\n                    balance -= event['amount']\n            else:\n                kind = 'credit'\n                if event['status'] != 'failed':\n                    balance -= event['amount'] - event['fee']\n        elif 'direction' in event:\n            kind = 'payment'\n            if event['direction'] == 'to-participant':\n                balance -= event['amount']\n            else:\n                assert event['direction'] == 'to-team'\n                balance += event['amount']\n        else:\n            kind = 'transfer'\n            if event['tippee'] == username:\n                balance -= event['amount']\n            else:\n                balance += event['amount']\n        event['kind'] = kind\n\n        yield event\n\n    yield dict(kind='day-close', balance=balance)\n\n\ndef export_history(participant, year, mode, key, back_as='namedtuple', require_key=False):\n    db = participant.db\n    params = dict(username=participant.username, year=year)\n    out = {}\n    if mode == 'aggregate':\n        out['given'] = lambda: db.all(\"\"\"\n            SELECT tippee, sum(amount) AS amount\n              FROM transfers\n             WHERE tipper = %(username)s\n               AND extract(year from timestamp) = %(year)s\n          GROUP BY tippee\n        \"\"\", params, back_as=back_as)\n        out['taken'] = lambda: db.all(\"\"\"\n            SELECT tipper AS team, sum(amount) AS amount\n              FROM transfers\n             WHERE tippee = %(username)s\n               AND context = 'take'\n               AND extract(year from timestamp) = %(year)s\n          GROUP BY tipper\n        \"\"\", params, back_as=back_as)\n    else:\n        out['exchanges'] = lambda: db.all(\"\"\"\n            SELECT timestamp, amount, fee, status, note\n              FROM exchanges\n             WHERE participant = %(username)s\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n        out['given'] = lambda: db.all(\"\"\"\n            SELECT timestamp, tippee, amount, context\n              FROM transfers\n             WHERE tipper = %(username)s\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n        out['taken'] = lambda: db.all(\"\"\"\n            SELECT timestamp, tipper AS team, amount\n              FROM transfers\n             WHERE tippee = %(username)s\n               AND context = 'take'\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n        out['received'] = lambda: db.all(\"\"\"\n            SELECT timestamp, amount, context\n              FROM transfers\n             WHERE tippee = %(username)s\n               AND context NOT IN ('take', 'take-over')\n               AND extract(year from timestamp) = %(year)s\n          ORDER BY timestamp ASC\n        \"\"\", params, back_as=back_as)\n\n    if key:\n        try:\n            return out[key]()\n        except KeyError:\n            raise Response(400, \"bad key `%s`\" % key)\n    elif require_key:\n        raise Response(400, \"missing `key` parameter\")\n    else:\n        return {k: v() for k, v in out.items()}\n", "path": "gratipay/utils/history.py"}]}