internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: gratipay/gratipay.com

Issue:
viewing history for a closed account 500s
https://app.getsentry.com/gratipay/gratipay-com/group/62465010/



Discussion:

Resolving patch:
diff --git a/gratipay/utils/history.py b/gratipay/utils/history.py
--- a/gratipay/utils/history.py
+++ b/gratipay/utils/history.py
@@ -8,7 +8,8 @@
 def get_end_of_year_balance(db, participant, year, current_year):
     if year == current_year:
         return participant.balance
-    if year < participant.claimed_time.year:
+    start = participant.claimed_time or participant.ctime
+    if year < start.year:
         return Decimal('0.00')
 
     balance = db.one("""


Test patch:
diff --git a/tests/py/test_history.py b/tests/py/test_history.py
--- a/tests/py/test_history.py
+++ b/tests/py/test_history.py
@@ -116,6 +116,24 @@ def test_get_end_of_year_balance(self):
         assert balance == 10
 
 
+class TestHistoryPage(Harness):
+
+    def setUp(self):
+        Harness.setUp(self)
+        make_history(self)
+
+    def test_participant_can_view_history(self):
+        assert self.client.GET('/alice/history/', auth_as='alice').code == 200
+
+    def test_admin_can_view_closed_participant_history(self):
+        self.make_participant('bob', claimed_time='now', is_admin=True)
+
+        self.alice.set_tip_to('bob', '1')
+        self.alice.close('downstream')
+
+        response = self.client.GET('/alice/history/?year=%s' % self.past_year, auth_as='bob')
+        assert "automatic charge" in response.body
+
 class TestExport(Harness):
 
     def setUp(self):
Repository: gratipay/gratipay.com

Issue:
respect $10 minimum in charge failure notification
From [FD1980](https://gratipay.freshdesk.com/helpdesk/tickets/1980) it appears that we're not doing this.

![screen shot 2015-04-17 at 10 38 40 am](https://cloud.githubusercontent.com/assets/134455/7204141/f5bea376-e4ed-11e4-8aa0-5247af11ac71.png)



Discussion:

Resolving patch:
diff --git a/gratipay/billing/exchanges.py b/gratipay/billing/exchanges.py
--- a/gratipay/billing/exchanges.py
+++ b/gratipay/billing/exchanges.py
@@ -179,6 +179,7 @@ def create_card_hold(db, participant, amount):
     # ===============
 
     cents, amount_str, charge_amount, fee = _prep_hit(amount)
+    amount = charge_amount - fee
     msg = "Holding " + amount_str + " on Balanced for " + username + " ... "
 
     hold = None


Test patch:
diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py
--- a/tests/py/test_billing_exchanges.py
+++ b/tests/py/test_billing_exchanges.py
@@ -77,7 +77,8 @@ def test_create_card_hold_failure(self, tfh):
         assert error == "Foobar()"
         exchange = self.db.one("SELECT * FROM exchanges")
         assert exchange
-        assert exchange.amount
+        assert exchange.amount == D('9.41')
+        assert exchange.fee == D('0.59')
         assert exchange.status == 'failed'
         janet = Participant.from_id(self.janet.id)
         assert self.janet.get_credit_card_error() == 'Foobar()'
Repository: gratipay/gratipay.com

Issue:
revenue model is '' for everyone
cf. #3479



Discussion:

Resolving patch:
diff --git a/gratipay/models/team.py b/gratipay/models/team.py
--- a/gratipay/models/team.py
+++ b/gratipay/models/team.py
@@ -47,18 +47,22 @@ def _from_thing(cls, thing, value):
         """.format(thing), (value,))
 
     @classmethod
-    def create_new(cls, owner, fields):
+    def insert(cls, owner, **fields):
+        fields['slug_lower'] = fields['slug'].lower()
+        fields['owner'] = owner.username
         return cls.db.one("""
 
             INSERT INTO teams
-                        (slug, slug_lower, name, homepage, product_or_service,
-                         getting_involved, getting_paid, owner)
-                 VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
+                        (slug, slug_lower, name, homepage,
+                         product_or_service, revenue_model, getting_involved, getting_paid,
+                         owner)
+                 VALUES (%(slug)s, %(slug_lower)s, %(name)s, %(homepage)s,
+                         %(product_or_service)s, %(revenue_model)s, %(getting_involved)s,
+                            %(getting_paid)s,
+                         %(owner)s)
               RETURNING teams.*::teams
 
-        """, (fields['slug'], fields['slug'].lower(), fields['name'], fields['homepage'],
-              fields['product_or_service'], fields['getting_involved'], fields['getting_paid'],
-              owner.username))
+        """, fields)
 
     def get_og_title(self):
         out = self.name


Test patch:
diff --git a/tests/py/test_teams.py b/tests/py/test_teams.py
--- a/tests/py/test_teams.py
+++ b/tests/py/test_teams.py
@@ -13,7 +13,7 @@ class TestNewTeams(Harness):
         'homepage': 'http://gratipay.com/',
         'agree_terms': 'true',
         'product_or_service': 'We make widgets.',
-        'revenue_model': 'People pay us',
+        'revenue_model': 'People pay us.',
         'getting_involved': 'People do stuff.',
         'getting_paid': 'We pay people.'
     }
@@ -46,6 +46,17 @@ def test_can_create_new_team(self):
         assert team
         assert team.owner == 'alice'
 
+    def test_all_fields_persist(self):
+        self.make_participant('alice', claimed_time='now', email_address='', last_ach_result='')
+        self.post_new(dict(self.valid_data))
+        team = Team.from_slug('gratiteam')
+        assert team.name == 'Gratiteam'
+        assert team.homepage == 'http://gratipay.com/'
+        assert team.product_or_service == 'We make widgets.'
+        assert team.revenue_model == 'People pay us.'
+        assert team.getting_involved == 'People do stuff.'
+        assert team.getting_paid == 'We pay people.'
+
     def test_401_for_anon_creating_new_team(self):
         self.post_new(self.valid_data, auth_as=None, expected=401)
         assert self.db.one("SELECT COUNT(*) FROM teams") == 0
Repository: gratipay/gratipay.com

Issue:
"Your credit card is about to expire!" banner shows falsely.
When I login, I see red "Your credit card is about to expire!" banner on top of the page. If I follow the link in it, I can see that my card is due to be expired in 2 years.



Discussion:
Hmm ... thanks for the report, @YorikSar.


Resolving patch:
diff --git a/gratipay/models/participant.py b/gratipay/models/participant.py
--- a/gratipay/models/participant.py
+++ b/gratipay/models/participant.py
@@ -33,6 +33,7 @@
     TooManyEmailAddresses,
 )
 
+from gratipay.billing.instruments import CreditCard
 from gratipay.models import add_event
 from gratipay.models.account_elsewhere import AccountElsewhere
 from gratipay.models.exchange_route import ExchangeRoute
@@ -619,10 +620,10 @@ def add_signin_notifications(self):
             self.add_notification('credit_card_expires')
 
     def credit_card_expiring(self):
-        route = ExchangeRoute.from_network(self, 'balanced-cc')
+        route = ExchangeRoute.from_network(self, 'braintree-cc')
         if not route:
             return
-        card = balanced.Card.fetch(route.address)
+        card = CreditCard.from_route(route)
         year, month = card.expiration_year, card.expiration_month
         if not (year and month):
             return False


Test patch:
diff --git a/tests/py/test_participant.py b/tests/py/test_participant.py
--- a/tests/py/test_participant.py
+++ b/tests/py/test_participant.py
@@ -9,6 +9,7 @@
 
 from aspen.utils import utcnow
 from gratipay import NotSane
+from gratipay.billing.instruments import CreditCard
 from gratipay.exceptions import (
     UsernameIsEmpty,
     UsernameTooLong,
@@ -559,6 +560,31 @@ def test_giving_is_updated_when_credit_card_fails(self, btd):
         assert Participant.from_username('alice').giving == Decimal('0.00')
         assert Team.from_slug(team.slug).receiving == Decimal('0.00')
 
+    # credit_card_expiring
+
+    def test_credit_card_expiring_no_card(self):
+        alice = self.make_participant('alice', claimed_time='now')
+        assert alice.credit_card_expiring() == None
+
+    @mock.patch.object(CreditCard, "from_route")
+    def test_credit_card_expiring_valid_card(self, cc):
+        alice = self.make_participant('alice', claimed_time='now', last_bill_result='')
+        cc.return_value = CreditCard(
+            expiration_year=2050,
+            expiration_month=12
+        )
+
+        assert alice.credit_card_expiring() == False
+
+    @mock.patch.object(CreditCard, "from_route")
+    def test_credit_card_expiring_expired_card(self, cc):
+        alice = self.make_participant('alice', claimed_time='now', last_bill_result='')
+        cc.return_value = CreditCard(
+            expiration_year=2010,
+            expiration_month=12
+        )
+
+        assert alice.credit_card_expiring() == True
 
     # get_age_in_seconds - gais
Repository: gratipay/gratipay.com

Issue:
Receiving includes tips from ~users with bad credit card info
From https://gratipay.freshdesk.com/helpdesk/tickets/3240.

~user is seeing a higher number than he is receiving weekly.  This is not due to arrears, because the credit card info for one of the people tipping his team is bad.  
- Where is the documentation for how the receiving number is calculated?  It's some sort of moving average, right?
- Is this the desired behavior?



Discussion:
!m @rohitpaulk @mattbk 

I'm noticing that the `receiving` on teams has been fixed, but the `taking` values on team owners are still stale. Reopening this, looking into it.


Resolving patch:
diff --git a/gratipay/models/team.py b/gratipay/models/team.py
--- a/gratipay/models/team.py
+++ b/gratipay/models/team.py
@@ -129,7 +129,7 @@ def update_receiving(self, cursor=None):
 
         # This next step is easy for now since we don't have payroll.
         from gratipay.models.participant import Participant
-        Participant.from_username(self.owner).update_taking()
+        Participant.from_username(self.owner).update_taking(cursor or self.db)
 
         self.set_attributes( receiving=r.receiving
                            , nreceiving_from=r.nreceiving_from
diff --git a/sql/branch.py b/sql/branch.py
new file mode 100644
--- /dev/null
+++ b/sql/branch.py
@@ -0,0 +1,17 @@
+import sys
+
+from gratipay import wireup
+from gratipay.models.participant import Participant
+
+db = wireup.db(wireup.env())
+
+teams = db.all("""
+    SELECT t.*::teams
+      FROM teams t
+""")
+
+for team in teams:
+    print("Updating team %s" % team.slug)
+    Participant.from_username(team.owner).update_taking()
+
+print("Done!")


Test patch:
diff --git a/tests/py/test_participant.py b/tests/py/test_participant.py
--- a/tests/py/test_participant.py
+++ b/tests/py/test_participant.py
@@ -543,11 +543,13 @@ def test_giving_is_updated_when_credit_card_fails(self, btd):
 
         assert alice.giving == Decimal('5.00')
         assert Team.from_slug(team.slug).receiving == Decimal('5.00')
+        assert Participant.from_username(team.owner).taking == Decimal('5.00')
 
         ExchangeRoute.from_network(alice, 'braintree-cc').update_error("Card expired")
 
         assert Participant.from_username('alice').giving == Decimal('0.00')
         assert Team.from_slug(team.slug).receiving == Decimal('0.00')
+        assert Participant.from_username(team.owner).taking == Decimal('0.00')
 
     # credit_card_expiring
Repository: gratipay/gratipay.com

Issue:
API for Team receiving amount
From @espadrine at https://github.com/gratipay/gratipay.com/commit/26a53d6553238cfbdfde65acfce96bc35d8ecd29#commitcomment-12965748: 

> Are there plans to give the amount received by a team as well?
> 
> Currently, I'd expect all Gratipay badges to show that the amount they receive is "anonymous".
#### Notify
- [ ] https://gratipay.freshdesk.com/helpdesk/tickets/2923



Discussion:
Just sent an email about this:

> Before I could go to https://gratipay.com/bevry/public.json and there would be a receiving property that would have the amount Bevry is receiving, it currently redirects to https://gratipay.com/~/Bevry/public.json and the "receiving" field no longer exists, instead there is a new "taking" field, but it is empty, whereas https://gratipay.com/bevry reports that the Bevry team is receiving $1-2/week

Update: Seems that https://gratipay.com/bevry/public.json is redirecting to the user bevry’s json file, instead of giving me the team’s amount.

Should be ticket https://gratipay.freshdesk.com/helpdesk/tickets/3007 

We used that API on our website: http://bevry.me - which I'll need to fallback to a last known good value. [Code we used.](https://github.com/bevry/website/blob/aec80bbac571bc1b3897728dbb77f5d0eb987b20/docpad.coffee#L21-L25)

+1 @fsquillace at https://github.com/gratipay/team-review/issues/96#issuecomment-152496519.


Resolving patch:
diff --git a/gratipay/models/team.py b/gratipay/models/team.py
--- a/gratipay/models/team.py
+++ b/gratipay/models/team.py
@@ -144,6 +144,19 @@ def status(self):
                , True: 'approved'
                 }[self.is_approved]
 
+    def to_dict(self):
+        return {
+            'homepage': self.homepage,
+            'name': self.name,
+            'nreceiving_from': self.nreceiving_from,
+            'onboarding_url': self.onboarding_url,
+            'owner': '~' + self.owner,
+            'receiving': self.receiving,
+            'slug': self.slug,
+            'status': self.status,
+            'todo_url': self.todo_url
+        }
+
     def migrate_tips(self):
         payment_instructions = self.db.all("""
             SELECT pi.*


Test patch:
Repository: gratipay/gratipay.com

Issue:
Speed up tip migration script
Reticketed from https://github.com/gratipay/inside.gratipay.com/issues/468#issuecomment-171707621

When we wrote this script, we thought that we'd only be using it for a month or two. Now that it's here to stay - time to work on it a bit :) 

The script takes so long because we're pulling in _all_ approved teams. We could place a check in the script so that we only pull in those teams that satisfy the following criteria - 

1) Team owner must have 0+ tips from Gratipay 1.0
2) Team must have zero `payment_instructions` that have a `ctime` lesser than the team's `ctime` (i.e. migrated tips). We use the `ctime` attribute to differentiate `payment_instructions` that were created as a result of migrated tips vs created in Gratipay 2.0

https://github.com/gratipay/gratipay.com/blob/master/bin/migrate-tips.py#L6-L10



Discussion:

Resolving patch:
diff --git a/bin/migrate-tips.py b/bin/migrate-tips.py
--- a/bin/migrate-tips.py
+++ b/bin/migrate-tips.py
@@ -1,20 +1,27 @@
 from gratipay.wireup import db, env
-from gratipay.models.team import Team, AlreadyMigrated
+from gratipay.models.team import AlreadyMigrated
 
 db = db(env())
 
-slugs = db.all("""
-    SELECT slug
-      FROM teams
-     WHERE is_approved IS TRUE
+teams = db.all("""
+    SELECT distinct ON (t.slug) t.*::teams
+      FROM teams t
+      JOIN tips ON t.owner = tips.tippee    -- Only fetch teams whose owners had tips under Gratipay 1.0
+     WHERE t.is_approved IS TRUE            -- Only fetch approved teams
+       AND NOT EXISTS (                     -- Make sure tips haven't been migrated for any teams with same owner
+            SELECT 1
+              FROM payment_instructions pi
+              JOIN teams t2 ON t2.slug = pi.team
+             WHERE t2.owner = t.owner
+               AND pi.ctime < t2.ctime
+       )
 """)
 
-for slug in slugs:
-    team = Team.from_slug(slug)
+for team in teams:
     try:
         ntips = team.migrate_tips()
-        print("Migrated {} tip(s) for '{}'".format(ntips, slug))
+        print("Migrated {} tip(s) for '{}'".format(ntips, team.slug))
     except AlreadyMigrated:
-        print("'%s' already migrated." % slug)
+        print("'%s' already migrated." % team.slug)
 
 print("Done.")
diff --git a/gratipay/models/team.py b/gratipay/models/team.py
--- a/gratipay/models/team.py
+++ b/gratipay/models/team.py
@@ -162,8 +162,7 @@ def migrate_tips(self):
             SELECT pi.*
               FROM payment_instructions pi
               JOIN teams t ON t.slug = pi.team
-              JOIN participants p ON t.owner = p.username
-             WHERE p.username = %s
+             WHERE t.owner = %s
                AND pi.ctime < t.ctime
         """, (self.owner, ))
 


Test patch:
Repository: gratipay/gratipay.com

Issue:
Team list for users with multiple teams is poorly spaced
...if the team name is sort of long:
![2016-09-30_11-15-15](https://cloud.githubusercontent.com/assets/3729038/18998683/53f69f70-86ff-11e6-9134-05e32f5c0fd4.png)

(Screenshot captured with ShareX, who are on Gratipay! https://gratipay.com/sharex/)



Discussion:

Resolving patch:
diff --git a/gratipay/utils/icons.py b/gratipay/utils/icons.py
new file mode 100644
--- /dev/null
+++ b/gratipay/utils/icons.py
@@ -0,0 +1,5 @@
+STATUS_ICONS = { "approved": "&#xe008;"
+               , "unreviewed": "&#xe009;"
+               , "rejected": "&#xe010;"
+               , "featured": "&#xe9d9;"
+                }


Test patch:
Repository: gratipay/gratipay.com

Issue:
Fix deploy.sh regression
[Looks](https://github.com/gratipay/gratipay.com/pull/4345#issuecomment-284062013) like #4345 introduced a regression in `deploy.sh`:

```
[gratipay] $ ./deploy.sh 
Already up-to-date.
Traceback (most recent call last):
  File "gratipay/wireup.py", line 11, in <module>
    import aspen
  File "/Users/whit537/personal/gratipay/gratipay.com/env/lib/python2.7/site-packages/aspen/__init__.py", line 65, in <module>
    import pkg_resources
  File "/Users/whit537/personal/gratipay/gratipay.com/env/lib/python2.7/site-packages/pkg_resources/__init__.py", line 37, in <module>
    import email.parser
  File "/Users/whit537/personal/gratipay/gratipay.com/gratipay/email.py", line 5, in <module>
    from aspen.simplates.pagination import parse_specline, split_and_escape
  File "/Users/whit537/personal/gratipay/gratipay.com/env/lib/python2.7/site-packages/aspen/simplates/__init__.py", line 15, in <module>
    from .. import log
ImportError: cannot import name log
[gratipay] $
```


Discussion:
Ah! [Here](https://github.com/gratipay/gratipay.com/blob/796d36e914a44b88e60964416e754c0b473ca163/deploy.sh#L50-L52) is where we're hitting `wireup`.
I made a `test.sh` truncated from `deploy.sh`:

```sh
#!/usr/bin/env bash


# Fail on error
set -e


# Be somewhere predictable
cd "$(dirname "$0")"


# Helpers

yesno () {
    proceed=""
    while [ "$proceed" != "y" ]; do
        read -p"$1 (y/n) " proceed
        [ "$proceed" = "n" ] && return 1
    done
    return 0
}

require () {
    if [ ! "$(which "$1")" ]; then
        echo "The '$1' command was not found."
        exit 1
    fi
}


# Check that we have the required tools
require heroku
require git
require curl


# Check that the environment contains all required variables
heroku config -s -a gratipay | ./env/bin/honcho run -e /dev/stdin \
    ./env/bin/python gratipay/wireup.py
```
I ran it manually against the commits on #4345. The regression is in https://github.com/gratipay/gratipay.com/commit/b6e89a4696e7451af840c1ac49ca101dcf5526a7.

Resolving patch:
diff --git a/gratipay/wireup.py b/gratipay/wireup.py
--- a/gratipay/wireup.py
+++ b/gratipay/wireup.py
@@ -476,5 +476,6 @@ def env():
     return env
 
 
-if __name__ == '__main__':
+def __main__():
+    # deploy.sh uses this to validate production env config
     env()


Test patch:
Repository: gratipay/gratipay.com

Issue:
Add a check in deploy.sh for environment variables
When deploying #4438, I forgot to add the `CHECK_NPM_SYNC_EVERY` env var, and gratipay.com was down for around 3 minutes until I figured out what was wrong and fix it.

We should be able to detect this before deploying by adding a check to `deploy.sh`


Discussion:
I really thought we already had a check for that. :thinking: 
Okay, hitting this ([slack](https://gratipay.slackarchive.io/gratipay/page-98/ts-1494367793196309)) while deploying #4305. Regression due to #4365. The `-m gratipay.wireup` is not actually being run, so it *looked* like it worked for purposes of #4365 but of course is failing to keep us safe from envvar change errors. Lessee here ...
Got it! PR forthcoming ...

```
[gratipay] $ ./env/bin/honcho run -e defaults.env,local.env ./env/bin/python -m gratipay.wireup
[gratipay] $ heroku config -s -a gratipay | ./env/bin/honcho run -e /dev/stdin ./env/bin/python -m gratipay.wireup
pid-79859 thread-140735204086528 (MainThread) ==========================================
pid-79859 thread-140735204086528 (MainThread) Oh no! Gratipay.com needs these missing 
pid-79859 thread-140735204086528 (MainThread) environment variables:
pid-79859 thread-140735204086528 (MainThread)  
pid-79859 thread-140735204086528 (MainThread)   PROJECT_REVIEW_REPO
pid-79859 thread-140735204086528 (MainThread)   PROJECT_REVIEW_TOKEN
pid-79859 thread-140735204086528 (MainThread)   PROJECT_REVIEW_USERNAME
pid-79859 thread-140735204086528 (MainThread)  
pid-79859 thread-140735204086528 (MainThread) (Sorry, we must've started looking for 
pid-79859 thread-140735204086528 (MainThread) these since you last updated Gratipay!)
pid-79859 thread-140735204086528 (MainThread)  
pid-79859 thread-140735204086528 (MainThread) Running Gratipay locally? Edit ./local.env.
pid-79859 thread-140735204086528 (MainThread) Running the test suite? Edit ./tests/env.
pid-79859 thread-140735204086528 (MainThread)  
pid-79859 thread-140735204086528 (MainThread) See ./default_local.env for hints.
pid-79859 thread-140735204086528 (MainThread) ==========================================
Missing envvars: PROJECT_REVIEW_REPO, PROJECT_REVIEW_TOKEN, PROJECT_REVIEW_USERNAME.
[gratipay] $
```

Resolving patch:
diff --git a/gratipay/wireup.py b/gratipay/wireup.py
--- a/gratipay/wireup.py
+++ b/gratipay/wireup.py
@@ -441,6 +441,6 @@ def env():
     return env
 
 
-def __main__():
+if __name__ == '__main__':
     # deploy.sh uses this to validate production env config
     env()


Test patch: