We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Tweaking the take throttling algorithm
 We're trying to use liberapay for our catalyst community in Lille, that's why we got 100€ (https://liberapay.com/collectifcatalyst/) by week for our local community. We need people to take this money faster than starting with 1€ (and 2€ / 4 / 8 next week) as people are giving a lot of time now. Is that possible ? Could we get multiple way to distribute money in a team depending on how the project go (for example for a faster project, starting with 1€ is difficult).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `liberapay/models/_mixin_team.py`
Content:
```
1 """Teams are groups of participants.
2 """
3 from __future__ import division, print_function, unicode_literals
4 
5 from collections import OrderedDict
6 from decimal import Decimal, ROUND_UP
7 from statistics import median
8 
9 from liberapay.constants import D_CENT, D_UNIT, D_ZERO
10 
11 
12 class MemberLimitReached(Exception): pass
13 
14 
15 class InactiveParticipantAdded(Exception): pass
16 
17 
18 class MixinTeam(object):
19 
20     def invite(self, invitee, inviter):
21         assert self.kind == 'group'
22         with self.db.get_cursor() as c:
23             n_id = invitee.notify(
24                 'team_invite',
25                 team=self.username,
26                 team_url=self.url(),
27                 inviter=inviter.username,
28             )
29             payload = dict(invitee=invitee.id, notification_id=n_id)
30             self.add_event(c, 'invite', payload, inviter.id)
31 
32     def add_member(self, member, cursor=None):
33         """Add a member to this team.
34         """
35         if len(self.get_current_takes()) == 149:
36             raise MemberLimitReached
37         if member.status != 'active':
38             raise InactiveParticipantAdded
39         self.set_take_for(member, D_ZERO, self, cursor=cursor)
40 
41     def remove_all_members(self, cursor=None):
42         (cursor or self.db).run("""
43             INSERT INTO takes (ctime, member, team, amount, recorder) (
44                 SELECT ctime, member, %(id)s, NULL, %(id)s
45                   FROM current_takes
46                  WHERE team=%(id)s
47             );
48         """, dict(id=self.id))
49 
50     def member_of(self, team):
51         """Given a Participant object, return a boolean.
52         """
53         assert team.kind == 'group'
54         return self.db.one("""
55             SELECT true
56               FROM current_takes
57              WHERE team=%s AND member=%s
58         """, (team.id, self.id), default=False)
59 
60     def get_takes_last_week(self):
61         """Get the users' nominal takes last week. Used in throttling.
62         """
63         assert self.kind == 'group'
64         takes = {t.member: t.amount for t in self.db.all("""
65 
66             SELECT DISTINCT ON (member) member, amount, mtime
67               FROM takes
68              WHERE team=%s
69                AND mtime < (
70                        SELECT ts_start
71                          FROM paydays
72                         WHERE ts_end > ts_start
73                      ORDER BY ts_start DESC LIMIT 1
74                    )
75           ORDER BY member, mtime DESC
76 
77         """, (self.id,)) if t.amount}
78         takes['_relative_min'] = median(takes.values() or (0,)) ** Decimal('0.7')
79         return takes
80 
81     def get_take_for(self, member):
82         """Return the nominal take for this member, or None.
83         """
84         return self.db.one(
85             "SELECT amount FROM current_takes WHERE member = %s AND team = %s",
86             (member.id, self.id)
87         )
88 
89     def compute_max_this_week(self, member_id, last_week):
90         """2x the member's take last week, or a minimum based on last week's
91         median take, or current income divided by the number of members if takes
92         were zero last week, or 1.
93         """
94         return max(
95             last_week.get(member_id, 0) * 2,
96             last_week['_relative_min'] or self.receiving / self.nmembers,
97             D_UNIT
98         )
99 
100     def set_take_for(self, member, take, recorder, check_max=True, cursor=None):
101         """Sets member's take from the team pool.
102         """
103         assert self.kind == 'group'
104 
105         if recorder.id != self.id:
106             cur_take = self.get_take_for(member)
107             if cur_take is None:
108                 return None
109 
110         if not isinstance(take, (None.__class__, Decimal)):
111             take = Decimal(take)
112 
113         if take and check_max and take > 1:
114             last_week = self.get_takes_last_week()
115             max_this_week = self.compute_max_this_week(member.id, last_week)
116             if take > max_this_week:
117                 take = max_this_week
118 
119         with self.db.get_cursor(cursor) as cursor:
120             # Lock to avoid race conditions
121             cursor.run("LOCK TABLE takes IN EXCLUSIVE MODE")
122             # Compute the current takes
123             old_takes = self.compute_actual_takes(cursor)
124             # Insert the new take
125             cursor.run("""
126 
127                 INSERT INTO takes (ctime, member, team, amount, recorder)
128                  VALUES ( COALESCE (( SELECT ctime
129                                         FROM takes
130                                        WHERE member=%(member)s
131                                          AND team=%(team)s
132                                        LIMIT 1
133                                      ), CURRENT_TIMESTAMP)
134                         , %(member)s
135                         , %(team)s
136                         , %(amount)s
137                         , %(recorder)s
138                          )
139 
140             """, dict(member=member.id, team=self.id, amount=take,
141                       recorder=recorder.id))
142             # Compute the new takes
143             new_takes = self.compute_actual_takes(cursor)
144             # Update receiving amounts in the participants table
145             self.update_taking(old_takes, new_takes, cursor, member)
146             # Update is_funded on member's tips
147             member.update_giving(cursor)
148 
149         return take
150 
151     def update_taking(self, old_takes, new_takes, cursor=None, member=None):
152         """Update `taking` amounts based on the difference between `old_takes`
153         and `new_takes`.
154         """
155         for p_id in set(old_takes.keys()).union(new_takes.keys()):
156             old = old_takes.get(p_id, {}).get('actual_amount', D_ZERO)
157             new = new_takes.get(p_id, {}).get('actual_amount', D_ZERO)
158             diff = new - old
159             if diff != 0:
160                 (cursor or self.db).run("""
161                     UPDATE participants
162                        SET taking = (taking + %(diff)s)
163                          , receiving = (receiving + %(diff)s)
164                      WHERE id=%(p_id)s
165                 """, dict(p_id=p_id, diff=diff))
166             if member and p_id == member.id:
167                 r = (cursor or self.db).one(
168                     "SELECT taking, receiving FROM participants WHERE id = %s",
169                     (p_id,)
170                 )
171                 member.set_attributes(**r._asdict())
172 
173     def get_current_takes(self, cursor=None):
174         """Return a list of member takes for a team.
175         """
176         assert self.kind == 'group'
177         TAKES = """
178             SELECT p.id AS member_id, p.username AS member_name, p.avatar_url
179                  , (p.mangopay_user_id IS NOT NULL) AS is_identified
180                  , t.amount, t.ctime, t.mtime
181               FROM current_takes t
182               JOIN participants p ON p.id = member
183              WHERE t.team=%(team)s
184           ORDER BY p.username
185         """
186         records = (cursor or self.db).all(TAKES, dict(team=self.id))
187         return [r._asdict() for r in records]
188 
189     def compute_actual_takes(self, cursor=None):
190         """Get the takes, compute the actual amounts, and return an OrderedDict.
191         """
192         actual_takes = OrderedDict()
193         nominal_takes = self.get_current_takes(cursor=cursor)
194         balance = self.receiving
195         total_takes = sum(t['amount'] for t in nominal_takes if t['is_identified'])
196         ratio = min(balance / total_takes, 1) if total_takes else 0
197         for take in nominal_takes:
198             nominal = take['nominal_take'] = take.pop('amount')
199             actual = take['actual_amount'] = min(
200                 (nominal * ratio).quantize(D_CENT, rounding=ROUND_UP),
201                 balance
202             ) if take['is_identified'] else D_ZERO
203             balance -= actual
204             actual_takes[take['member_id']] = take
205         actual_takes.leftover = balance
206         return actual_takes
207 
208     @property
209     def nmembers(self):
210         assert self.kind == 'group'
211         return self.db.one("""
212             SELECT COUNT(*)
213               FROM current_takes
214              WHERE team=%s
215         """, (self.id,))
216 
217     def get_members(self):
218         """Return an OrderedDict of member dicts.
219         """
220         takes = self.compute_actual_takes()
221         last_week = self.get_takes_last_week()
222         members = OrderedDict()
223         members.leftover = takes.leftover
224         for take in takes.values():
225             member = {}
226             m_id = member['id'] = take['member_id']
227             member['username'] = take['member_name']
228             member['nominal_take'] = take['nominal_take']
229             member['actual_amount'] = take['actual_amount']
230             member['last_week'] = last_week.get(m_id, D_ZERO)
231             member['max_this_week'] = self.compute_max_this_week(m_id, last_week)
232             members[member['id']] = member
233         return members
234 
235     @property
236     def closed_by(self):
237         assert self.status == 'closed'
238         return self.db.one("""
239             SELECT member
240               FROM takes
241              WHERE team = %s
242           ORDER BY mtime DESC
243              LIMIT 1
244         """, (self.id,))
245
```
--- 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/liberapay/models/_mixin_team.py b/liberapay/models/_mixin_team.py
--- a/liberapay/models/_mixin_team.py
+++ b/liberapay/models/_mixin_team.py
@@ -75,7 +75,6 @@
           ORDER BY member, mtime DESC
 
         """, (self.id,)) if t.amount}
-        takes['_relative_min'] = median(takes.values() or (0,)) ** Decimal('0.7')
         return takes
 
     def get_take_for(self, member):
@@ -87,13 +86,20 @@
         )
 
     def compute_max_this_week(self, member_id, last_week):
-        """2x the member's take last week, or a minimum based on last week's
-        median take, or current income divided by the number of members if takes
-        were zero last week, or 1.
+        """2x the member's take last week, or the member's take last week + a
+        proportional share of the leftover, or a minimum based on last week's
+        median take, or 1.
         """
+        sum_last_week = sum(last_week.values())
+        initial_leftover = self.receiving - sum_last_week
+        nonzero_last_week = [a for a in last_week.values() if a]
+        member_last_week = last_week.get(member_id, 0)
+        leftover_share = member_last_week / (sum_last_week or Decimal('inf'))
+        leftover_share = max(leftover_share, D_UNIT / self.nmembers)
         return max(
-            last_week.get(member_id, 0) * 2,
-            last_week['_relative_min'] or self.receiving / self.nmembers,
+            member_last_week * 2,
+            member_last_week + initial_leftover * leftover_share,
+            median(nonzero_last_week or (0,)),
             D_UNIT
         )

{"golden_diff": "diff --git a/liberapay/models/_mixin_team.py b/liberapay/models/_mixin_team.py\n--- a/liberapay/models/_mixin_team.py\n+++ b/liberapay/models/_mixin_team.py\n@@ -75,7 +75,6 @@\n           ORDER BY member, mtime DESC\n \n         \"\"\", (self.id,)) if t.amount}\n-        takes['_relative_min'] = median(takes.values() or (0,)) ** Decimal('0.7')\n         return takes\n \n     def get_take_for(self, member):\n@@ -87,13 +86,20 @@\n         )\n \n     def compute_max_this_week(self, member_id, last_week):\n-        \"\"\"2x the member's take last week, or a minimum based on last week's\n-        median take, or current income divided by the number of members if takes\n-        were zero last week, or 1.\n+        \"\"\"2x the member's take last week, or the member's take last week + a\n+        proportional share of the leftover, or a minimum based on last week's\n+        median take, or 1.\n         \"\"\"\n+        sum_last_week = sum(last_week.values())\n+        initial_leftover = self.receiving - sum_last_week\n+        nonzero_last_week = [a for a in last_week.values() if a]\n+        member_last_week = last_week.get(member_id, 0)\n+        leftover_share = member_last_week / (sum_last_week or Decimal('inf'))\n+        leftover_share = max(leftover_share, D_UNIT / self.nmembers)\n         return max(\n-            last_week.get(member_id, 0) * 2,\n-            last_week['_relative_min'] or self.receiving / self.nmembers,\n+            member_last_week * 2,\n+            member_last_week + initial_leftover * leftover_share,\n+            median(nonzero_last_week or (0,)),\n             D_UNIT\n         )\n", "issue": "Tweaking the take throttling algorithm\n We're trying to use liberapay for our catalyst community in Lille, that's why we got 100\u20ac (https://liberapay.com/collectifcatalyst/) by week for our local community. We need people to take this money faster than starting with 1\u20ac (and 2\u20ac / 4 / 8 next week) as people are giving a lot of time now. Is that possible ? Could we get multiple way to distribute money in a team depending on how the project go (for example for a faster project, starting with 1\u20ac is difficult).\n\n", "before_files": [{"content": "\"\"\"Teams are groups of participants.\n\"\"\"\nfrom __future__ import division, print_function, unicode_literals\n\nfrom collections import OrderedDict\nfrom decimal import Decimal, ROUND_UP\nfrom statistics import median\n\nfrom liberapay.constants import D_CENT, D_UNIT, D_ZERO\n\n\nclass MemberLimitReached(Exception): pass\n\n\nclass InactiveParticipantAdded(Exception): pass\n\n\nclass MixinTeam(object):\n\n    def invite(self, invitee, inviter):\n        assert self.kind == 'group'\n        with self.db.get_cursor() as c:\n            n_id = invitee.notify(\n                'team_invite',\n                team=self.username,\n                team_url=self.url(),\n                inviter=inviter.username,\n            )\n            payload = dict(invitee=invitee.id, notification_id=n_id)\n            self.add_event(c, 'invite', payload, inviter.id)\n\n    def add_member(self, member, cursor=None):\n        \"\"\"Add a member to this team.\n        \"\"\"\n        if len(self.get_current_takes()) == 149:\n            raise MemberLimitReached\n        if member.status != 'active':\n            raise InactiveParticipantAdded\n        self.set_take_for(member, D_ZERO, self, cursor=cursor)\n\n    def remove_all_members(self, cursor=None):\n        (cursor or self.db).run(\"\"\"\n            INSERT INTO takes (ctime, member, team, amount, recorder) (\n                SELECT ctime, member, %(id)s, NULL, %(id)s\n                  FROM current_takes\n                 WHERE team=%(id)s\n            );\n        \"\"\", dict(id=self.id))\n\n    def member_of(self, team):\n        \"\"\"Given a Participant object, return a boolean.\n        \"\"\"\n        assert team.kind == 'group'\n        return self.db.one(\"\"\"\n            SELECT true\n              FROM current_takes\n             WHERE team=%s AND member=%s\n        \"\"\", (team.id, self.id), default=False)\n\n    def get_takes_last_week(self):\n        \"\"\"Get the users' nominal takes last week. Used in throttling.\n        \"\"\"\n        assert self.kind == 'group'\n        takes = {t.member: t.amount for t in self.db.all(\"\"\"\n\n            SELECT DISTINCT ON (member) member, amount, mtime\n              FROM takes\n             WHERE team=%s\n               AND mtime < (\n                       SELECT ts_start\n                         FROM paydays\n                        WHERE ts_end > ts_start\n                     ORDER BY ts_start DESC LIMIT 1\n                   )\n          ORDER BY member, mtime DESC\n\n        \"\"\", (self.id,)) if t.amount}\n        takes['_relative_min'] = median(takes.values() or (0,)) ** Decimal('0.7')\n        return takes\n\n    def get_take_for(self, member):\n        \"\"\"Return the nominal take for this member, or None.\n        \"\"\"\n        return self.db.one(\n            \"SELECT amount FROM current_takes WHERE member = %s AND team = %s\",\n            (member.id, self.id)\n        )\n\n    def compute_max_this_week(self, member_id, last_week):\n        \"\"\"2x the member's take last week, or a minimum based on last week's\n        median take, or current income divided by the number of members if takes\n        were zero last week, or 1.\n        \"\"\"\n        return max(\n            last_week.get(member_id, 0) * 2,\n            last_week['_relative_min'] or self.receiving / self.nmembers,\n            D_UNIT\n        )\n\n    def set_take_for(self, member, take, recorder, check_max=True, cursor=None):\n        \"\"\"Sets member's take from the team pool.\n        \"\"\"\n        assert self.kind == 'group'\n\n        if recorder.id != self.id:\n            cur_take = self.get_take_for(member)\n            if cur_take is None:\n                return None\n\n        if not isinstance(take, (None.__class__, Decimal)):\n            take = Decimal(take)\n\n        if take and check_max and take > 1:\n            last_week = self.get_takes_last_week()\n            max_this_week = self.compute_max_this_week(member.id, last_week)\n            if take > max_this_week:\n                take = max_this_week\n\n        with self.db.get_cursor(cursor) as cursor:\n            # Lock to avoid race conditions\n            cursor.run(\"LOCK TABLE takes IN EXCLUSIVE MODE\")\n            # Compute the current takes\n            old_takes = self.compute_actual_takes(cursor)\n            # Insert the new take\n            cursor.run(\"\"\"\n\n                INSERT INTO takes (ctime, member, team, amount, recorder)\n                 VALUES ( COALESCE (( SELECT ctime\n                                        FROM takes\n                                       WHERE member=%(member)s\n                                         AND team=%(team)s\n                                       LIMIT 1\n                                     ), CURRENT_TIMESTAMP)\n                        , %(member)s\n                        , %(team)s\n                        , %(amount)s\n                        , %(recorder)s\n                         )\n\n            \"\"\", dict(member=member.id, team=self.id, amount=take,\n                      recorder=recorder.id))\n            # Compute the new takes\n            new_takes = self.compute_actual_takes(cursor)\n            # Update receiving amounts in the participants table\n            self.update_taking(old_takes, new_takes, cursor, member)\n            # Update is_funded on member's tips\n            member.update_giving(cursor)\n\n        return take\n\n    def update_taking(self, old_takes, new_takes, cursor=None, member=None):\n        \"\"\"Update `taking` amounts based on the difference between `old_takes`\n        and `new_takes`.\n        \"\"\"\n        for p_id in set(old_takes.keys()).union(new_takes.keys()):\n            old = old_takes.get(p_id, {}).get('actual_amount', D_ZERO)\n            new = new_takes.get(p_id, {}).get('actual_amount', D_ZERO)\n            diff = new - old\n            if diff != 0:\n                (cursor or self.db).run(\"\"\"\n                    UPDATE participants\n                       SET taking = (taking + %(diff)s)\n                         , receiving = (receiving + %(diff)s)\n                     WHERE id=%(p_id)s\n                \"\"\", dict(p_id=p_id, diff=diff))\n            if member and p_id == member.id:\n                r = (cursor or self.db).one(\n                    \"SELECT taking, receiving FROM participants WHERE id = %s\",\n                    (p_id,)\n                )\n                member.set_attributes(**r._asdict())\n\n    def get_current_takes(self, cursor=None):\n        \"\"\"Return a list of member takes for a team.\n        \"\"\"\n        assert self.kind == 'group'\n        TAKES = \"\"\"\n            SELECT p.id AS member_id, p.username AS member_name, p.avatar_url\n                 , (p.mangopay_user_id IS NOT NULL) AS is_identified\n                 , t.amount, t.ctime, t.mtime\n              FROM current_takes t\n              JOIN participants p ON p.id = member\n             WHERE t.team=%(team)s\n          ORDER BY p.username\n        \"\"\"\n        records = (cursor or self.db).all(TAKES, dict(team=self.id))\n        return [r._asdict() for r in records]\n\n    def compute_actual_takes(self, cursor=None):\n        \"\"\"Get the takes, compute the actual amounts, and return an OrderedDict.\n        \"\"\"\n        actual_takes = OrderedDict()\n        nominal_takes = self.get_current_takes(cursor=cursor)\n        balance = self.receiving\n        total_takes = sum(t['amount'] for t in nominal_takes if t['is_identified'])\n        ratio = min(balance / total_takes, 1) if total_takes else 0\n        for take in nominal_takes:\n            nominal = take['nominal_take'] = take.pop('amount')\n            actual = take['actual_amount'] = min(\n                (nominal * ratio).quantize(D_CENT, rounding=ROUND_UP),\n                balance\n            ) if take['is_identified'] else D_ZERO\n            balance -= actual\n            actual_takes[take['member_id']] = take\n        actual_takes.leftover = balance\n        return actual_takes\n\n    @property\n    def nmembers(self):\n        assert self.kind == 'group'\n        return self.db.one(\"\"\"\n            SELECT COUNT(*)\n              FROM current_takes\n             WHERE team=%s\n        \"\"\", (self.id,))\n\n    def get_members(self):\n        \"\"\"Return an OrderedDict of member dicts.\n        \"\"\"\n        takes = self.compute_actual_takes()\n        last_week = self.get_takes_last_week()\n        members = OrderedDict()\n        members.leftover = takes.leftover\n        for take in takes.values():\n            member = {}\n            m_id = member['id'] = take['member_id']\n            member['username'] = take['member_name']\n            member['nominal_take'] = take['nominal_take']\n            member['actual_amount'] = take['actual_amount']\n            member['last_week'] = last_week.get(m_id, D_ZERO)\n            member['max_this_week'] = self.compute_max_this_week(m_id, last_week)\n            members[member['id']] = member\n        return members\n\n    @property\n    def closed_by(self):\n        assert self.status == 'closed'\n        return self.db.one(\"\"\"\n            SELECT member\n              FROM takes\n             WHERE team = %s\n          ORDER BY mtime DESC\n             LIMIT 1\n        \"\"\", (self.id,))\n", "path": "liberapay/models/_mixin_team.py"}], "after_files": [{"content": "\"\"\"Teams are groups of participants.\n\"\"\"\nfrom __future__ import division, print_function, unicode_literals\n\nfrom collections import OrderedDict\nfrom decimal import Decimal, ROUND_UP\nfrom statistics import median\n\nfrom liberapay.constants import D_CENT, D_UNIT, D_ZERO\n\n\nclass MemberLimitReached(Exception): pass\n\n\nclass InactiveParticipantAdded(Exception): pass\n\n\nclass MixinTeam(object):\n\n    def invite(self, invitee, inviter):\n        assert self.kind == 'group'\n        with self.db.get_cursor() as c:\n            n_id = invitee.notify(\n                'team_invite',\n                team=self.username,\n                team_url=self.url(),\n                inviter=inviter.username,\n            )\n            payload = dict(invitee=invitee.id, notification_id=n_id)\n            self.add_event(c, 'invite', payload, inviter.id)\n\n    def add_member(self, member, cursor=None):\n        \"\"\"Add a member to this team.\n        \"\"\"\n        if len(self.get_current_takes()) == 149:\n            raise MemberLimitReached\n        if member.status != 'active':\n            raise InactiveParticipantAdded\n        self.set_take_for(member, D_ZERO, self, cursor=cursor)\n\n    def remove_all_members(self, cursor=None):\n        (cursor or self.db).run(\"\"\"\n            INSERT INTO takes (ctime, member, team, amount, recorder) (\n                SELECT ctime, member, %(id)s, NULL, %(id)s\n                  FROM current_takes\n                 WHERE team=%(id)s\n            );\n        \"\"\", dict(id=self.id))\n\n    def member_of(self, team):\n        \"\"\"Given a Participant object, return a boolean.\n        \"\"\"\n        assert team.kind == 'group'\n        return self.db.one(\"\"\"\n            SELECT true\n              FROM current_takes\n             WHERE team=%s AND member=%s\n        \"\"\", (team.id, self.id), default=False)\n\n    def get_takes_last_week(self):\n        \"\"\"Get the users' nominal takes last week. Used in throttling.\n        \"\"\"\n        assert self.kind == 'group'\n        takes = {t.member: t.amount for t in self.db.all(\"\"\"\n\n            SELECT DISTINCT ON (member) member, amount, mtime\n              FROM takes\n             WHERE team=%s\n               AND mtime < (\n                       SELECT ts_start\n                         FROM paydays\n                        WHERE ts_end > ts_start\n                     ORDER BY ts_start DESC LIMIT 1\n                   )\n          ORDER BY member, mtime DESC\n\n        \"\"\", (self.id,)) if t.amount}\n        return takes\n\n    def get_take_for(self, member):\n        \"\"\"Return the nominal take for this member, or None.\n        \"\"\"\n        return self.db.one(\n            \"SELECT amount FROM current_takes WHERE member = %s AND team = %s\",\n            (member.id, self.id)\n        )\n\n    def compute_max_this_week(self, member_id, last_week):\n        \"\"\"2x the member's take last week, or the member's take last week + a\n        proportional share of the leftover, or a minimum based on last week's\n        median take, or 1.\n        \"\"\"\n        sum_last_week = sum(last_week.values())\n        initial_leftover = self.receiving - sum_last_week\n        nonzero_last_week = [a for a in last_week.values() if a]\n        member_last_week = last_week.get(member_id, 0)\n        leftover_share = member_last_week / (sum_last_week or Decimal('inf'))\n        leftover_share = max(leftover_share, D_UNIT / self.nmembers)\n        return max(\n            member_last_week * 2,\n            member_last_week + initial_leftover * leftover_share,\n            median(nonzero_last_week or (0,)),\n            D_UNIT\n        )\n\n    def set_take_for(self, member, take, recorder, check_max=True, cursor=None):\n        \"\"\"Sets member's take from the team pool.\n        \"\"\"\n        assert self.kind == 'group'\n\n        if recorder.id != self.id:\n            cur_take = self.get_take_for(member)\n            if cur_take is None:\n                return None\n\n        if not isinstance(take, (None.__class__, Decimal)):\n            take = Decimal(take)\n\n        if take and check_max and take > 1:\n            last_week = self.get_takes_last_week()\n            max_this_week = self.compute_max_this_week(member.id, last_week)\n            if take > max_this_week:\n                take = max_this_week\n\n        with self.db.get_cursor(cursor) as cursor:\n            # Lock to avoid race conditions\n            cursor.run(\"LOCK TABLE takes IN EXCLUSIVE MODE\")\n            # Compute the current takes\n            old_takes = self.compute_actual_takes(cursor)\n            # Insert the new take\n            cursor.run(\"\"\"\n\n                INSERT INTO takes (ctime, member, team, amount, recorder)\n                 VALUES ( COALESCE (( SELECT ctime\n                                        FROM takes\n                                       WHERE member=%(member)s\n                                         AND team=%(team)s\n                                       LIMIT 1\n                                     ), CURRENT_TIMESTAMP)\n                        , %(member)s\n                        , %(team)s\n                        , %(amount)s\n                        , %(recorder)s\n                         )\n\n            \"\"\", dict(member=member.id, team=self.id, amount=take,\n                      recorder=recorder.id))\n            # Compute the new takes\n            new_takes = self.compute_actual_takes(cursor)\n            # Update receiving amounts in the participants table\n            self.update_taking(old_takes, new_takes, cursor, member)\n            # Update is_funded on member's tips\n            member.update_giving(cursor)\n\n        return take\n\n    def update_taking(self, old_takes, new_takes, cursor=None, member=None):\n        \"\"\"Update `taking` amounts based on the difference between `old_takes`\n        and `new_takes`.\n        \"\"\"\n        for p_id in set(old_takes.keys()).union(new_takes.keys()):\n            old = old_takes.get(p_id, {}).get('actual_amount', D_ZERO)\n            new = new_takes.get(p_id, {}).get('actual_amount', D_ZERO)\n            diff = new - old\n            if diff != 0:\n                (cursor or self.db).run(\"\"\"\n                    UPDATE participants\n                       SET taking = (taking + %(diff)s)\n                         , receiving = (receiving + %(diff)s)\n                     WHERE id=%(p_id)s\n                \"\"\", dict(p_id=p_id, diff=diff))\n            if member and p_id == member.id:\n                r = (cursor or self.db).one(\n                    \"SELECT taking, receiving FROM participants WHERE id = %s\",\n                    (p_id,)\n                )\n                member.set_attributes(**r._asdict())\n\n    def get_current_takes(self, cursor=None):\n        \"\"\"Return a list of member takes for a team.\n        \"\"\"\n        assert self.kind == 'group'\n        TAKES = \"\"\"\n            SELECT p.id AS member_id, p.username AS member_name, p.avatar_url\n                 , (p.mangopay_user_id IS NOT NULL) AS is_identified\n                 , t.amount, t.ctime, t.mtime\n              FROM current_takes t\n              JOIN participants p ON p.id = member\n             WHERE t.team=%(team)s\n          ORDER BY p.username\n        \"\"\"\n        records = (cursor or self.db).all(TAKES, dict(team=self.id))\n        return [r._asdict() for r in records]\n\n    def compute_actual_takes(self, cursor=None):\n        \"\"\"Get the takes, compute the actual amounts, and return an OrderedDict.\n        \"\"\"\n        actual_takes = OrderedDict()\n        nominal_takes = self.get_current_takes(cursor=cursor)\n        balance = self.receiving\n        total_takes = sum(t['amount'] for t in nominal_takes if t['is_identified'])\n        ratio = min(balance / total_takes, 1) if total_takes else 0\n        for take in nominal_takes:\n            nominal = take['nominal_take'] = take.pop('amount')\n            actual = take['actual_amount'] = min(\n                (nominal * ratio).quantize(D_CENT, rounding=ROUND_UP),\n                balance\n            ) if take['is_identified'] else D_ZERO\n            balance -= actual\n            actual_takes[take['member_id']] = take\n        actual_takes.leftover = balance\n        return actual_takes\n\n    @property\n    def nmembers(self):\n        assert self.kind == 'group'\n        return self.db.one(\"\"\"\n            SELECT COUNT(*)\n              FROM current_takes\n             WHERE team=%s\n        \"\"\", (self.id,))\n\n    def get_members(self):\n        \"\"\"Return an OrderedDict of member dicts.\n        \"\"\"\n        takes = self.compute_actual_takes()\n        last_week = self.get_takes_last_week()\n        members = OrderedDict()\n        members.leftover = takes.leftover\n        for take in takes.values():\n            member = {}\n            m_id = member['id'] = take['member_id']\n            member['username'] = take['member_name']\n            member['nominal_take'] = take['nominal_take']\n            member['actual_amount'] = take['actual_amount']\n            member['last_week'] = last_week.get(m_id, D_ZERO)\n            member['max_this_week'] = self.compute_max_this_week(m_id, last_week)\n            members[member['id']] = member\n        return members\n\n    @property\n    def closed_by(self):\n        assert self.status == 'closed'\n        return self.db.one(\"\"\"\n            SELECT member\n              FROM takes\n             WHERE team = %s\n          ORDER BY mtime DESC\n             LIMIT 1\n        \"\"\", (self.id,))\n", "path": "liberapay/models/_mixin_team.py"}]}