We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sign up with a Twitter account fails.
```
Internal server error, program!
Traceback (most recent call last):
  File "/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/algorithm.py", line 288, in run
    new_state = function(**deps.as_kwargs)
  File "/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/aspen/algorithms/website.py", line 88, in get_response_for_resource
    return {'response': resource.respond(request)}
  File "/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/aspen/resources/dynamic_resource.py", line 52, in respond
    exec self.pages[1] in context
  File "/home/sim6/www.gittip.com/www/on/twitter/associate.spt", line 77, in 
    account = twitter.TwitterAccount(website.db, user_info['id'], user_info)
  File "/home/sim6/www.gittip.com/gittip/elsewhere/__init__.py", line 80, in __init__
    typecheck(user_id, (int, unicode), user_info, (None, dict))
  File "/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/aspen/utils.py", line 377, in typecheck
    raise TypeError(msg)
TypeError: Check #1: 2304869623L is of type long, not one of: int, unicode.
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gittip/elsewhere/__init__.py`
Content:
```
1 """This subpackage contains functionality for working with accounts elsewhere.
2 """
3 from __future__ import print_function, unicode_literals
4 from collections import OrderedDict
5 
6 from aspen.utils import typecheck
7 from aspen import json
8 from psycopg2 import IntegrityError
9 
10 import gittip
11 from gittip.exceptions import ProblemChangingUsername, UnknownPlatform
12 from gittip.utils.username import reserve_a_random_username
13 
14 
15 ACTIONS = [u'opt-in', u'connect', u'lock', u'unlock']
16 
17 
18 # to add a new elsewhere/platform:
19 #  1) add its name (also the name of its module) to this list.
20 #     it's best to append it; this ordering is used in templates.
21 #  2) inherit from AccountElsewhere in the platform class
22 #
23 # platform_modules will populate the platform class automatically in configure-aspen.
24 platforms_ordered = (
25     'twitter',
26     'github',
27     'bitbucket',
28     'bountysource',
29     'venmo',
30     'openstreetmap'
31 )
32 
33 # init-time key setup ensures the future ordering of platform_classes will match
34 # platforms_ordered, since overwriting entries will maintain their order.
35 platform_classes = OrderedDict([(platform, None) for platform in platforms_ordered])
36 
37 
38 class _RegisterPlatformMeta(type):
39     """Tied to AccountElsewhere to enable registration by the platform field.
40     """
41 
42     def __new__(cls, name, bases, dct):
43         c = super(_RegisterPlatformMeta, cls).__new__(cls, name, bases, dct)
44 
45         # * register the platform
46         # * verify it was added at init-time
47         # * register the subclass's json encoder with aspen
48         c_platform = getattr(c, 'platform')
49         if name == 'AccountElsewhere':
50             pass
51         elif c_platform not in platform_classes:
52             raise UnknownPlatform(c_platform)  # has it been added to platform_classes init?
53         else:
54             platform_classes[c_platform] = c
55 
56             # aspen's json encoder registry does not take class hierarchies into account,
57             #  so we need to register the subclasses explicitly.
58             json.register_encoder(c, c.to_json_compatible_object)
59 
60         return c
61 
62 class AccountElsewhere(object):
63 
64     __metaclass__ = _RegisterPlatformMeta
65 
66     platform = None  # set in subclass
67 
68     # only fields in this set will be encoded
69     json_encode_field_whitelist = set([
70         'id', 'is_locked', 'participant', 'platform', 'user_id', 'user_info',
71     ])
72 
73     def __init__(self, db, user_id, user_info=None, existing_record=None):
74         """Either:
75         - Takes a user_id and user_info, and updates the database.
76 
77         Or:
78         - Takes a user_id and existing_record, and constructs a "model" object out of the record
79         """
80         typecheck(user_id, (int, unicode), user_info, (None, dict))
81         self.user_id = unicode(user_id)
82         self.db = db
83 
84         if user_info is not None:
85             a,b,c,d  = self.upsert(user_info)
86 
87             self.participant = a
88             self.is_claimed = b
89             self.is_locked = c
90             self.balance = d
91 
92             self.user_info = user_info
93 
94         # hack to make this into a weird pseudo-model that can share convenience methods
95         elif existing_record is not None:
96             self.participant = existing_record.participant
97             self.is_claimed, self.is_locked, self.balance = self.get_misc_info(self.participant)
98             self.user_info = existing_record.user_info
99             self.record = existing_record
100 
101     def to_json_compatible_object(self):
102         """
103         This is registered as an aspen.json encoder in configure-aspen
104         for all subclasses of this class.
105 
106         It only exports fields in the whitelist.
107         """
108         output = {k: v for (k,v) in self.record._asdict().items()
109                   if k in self.json_encode_field_whitelist}
110 
111         return output
112 
113     def set_is_locked(self, is_locked):
114         self.db.run("""
115 
116             UPDATE elsewhere
117                SET is_locked=%s
118              WHERE platform=%s AND user_id=%s
119 
120         """, (is_locked, self.platform, self.user_id))
121 
122 
123     def opt_in(self, desired_username):
124         """Given a desired username, return a User object.
125         """
126         from gittip.security.user import User
127 
128         self.set_is_locked(False)
129         user = User.from_username(self.participant)
130         user.sign_in()
131         assert not user.ANON, self.participant  # sanity check
132         if self.is_claimed:
133             newly_claimed = False
134         else:
135             newly_claimed = True
136             user.participant.set_as_claimed()
137             try:
138                 user.participant.change_username(desired_username)
139             except ProblemChangingUsername:
140                 pass
141         return user, newly_claimed
142 
143 
144     def upsert(self, user_info):
145         """Given a dict, return a tuple.
146 
147         User_id is an immutable unique identifier for the given user on the
148         given platform.  Username is the user's login/username on the given
149         platform. It is only used here for logging. Specifically, we don't
150         reserve their username for them on Gittip if they're new here. We give
151         them a random username here, and they'll have a chance to change it
152         if/when they opt in. User_id and username may or may not be the same.
153         User_info is a dictionary of profile info per the named platform.  All
154         platform dicts must have an id key that corresponds to the primary key
155         in the underlying table in our own db.
156 
157         The return value is a tuple: (username [unicode], is_claimed [boolean],
158         is_locked [boolean], balance [Decimal]).
159 
160         """
161         typecheck(user_info, dict)
162 
163 
164         # Insert the account if needed.
165         # =============================
166         # Do this with a transaction so that if the insert fails, the
167         # participant we reserved for them is rolled back as well.
168 
169         try:
170             with self.db.get_cursor() as cursor:
171                 _username = reserve_a_random_username(cursor)
172                 cursor.execute( "INSERT INTO elsewhere "
173                                 "(platform, user_id, participant) "
174                                 "VALUES (%s, %s, %s)"
175                               , (self.platform, self.user_id, _username)
176                                )
177         except IntegrityError:
178             pass
179 
180 
181         # Update their user_info.
182         # =======================
183         # Cast everything to unicode, because (I believe) hstore can take any
184         # type of value, but psycopg2 can't.
185         #
186         #   https://postgres.heroku.com/blog/past/2012/3/14/introducing_keyvalue_data_storage_in_heroku_postgres/
187         #   http://initd.org/psycopg/docs/extras.html#hstore-data-type
188         #
189         # XXX This clobbers things, of course, such as booleans. See
190         # /on/bitbucket/%username/index.html
191 
192         for k, v in user_info.items():
193             user_info[k] = unicode(v)
194 
195 
196         username = self.db.one("""
197 
198             UPDATE elsewhere
199                SET user_info=%s
200              WHERE platform=%s AND user_id=%s
201          RETURNING participant
202 
203         """, (user_info, self.platform, self.user_id))
204 
205         return (username,) + self.get_misc_info(username)
206 
207     def get_misc_info(self, username):
208         rec = self.db.one("""
209 
210             SELECT claimed_time, balance, is_locked
211               FROM participants
212               JOIN elsewhere
213                 ON participants.username=participant
214              WHERE platform=%s
215                AND participants.username=%s
216 
217         """, (self.platform, username))
218 
219         assert rec is not None  # sanity check
220 
221         return ( rec.claimed_time is not None
222                , rec.is_locked
223                , rec.balance
224                 )
225 
226     def set_oauth_tokens(self, access_token, refresh_token, expires):
227         """
228         Updates the elsewhere row with the given access token, refresh token, and Python datetime
229         """
230 
231         self.db.run("""
232             UPDATE elsewhere 
233             SET (access_token, refresh_token, expires) 
234             = (%s, %s, %s) 
235             WHERE platform=%s AND user_id=%s
236         """, (access_token, refresh_token, expires, self.platform, self.user_id))
237
```
--- 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/gittip/elsewhere/__init__.py b/gittip/elsewhere/__init__.py
--- a/gittip/elsewhere/__init__.py
+++ b/gittip/elsewhere/__init__.py
@@ -77,7 +77,7 @@
         Or:
         - Takes a user_id and existing_record, and constructs a "model" object out of the record
         """
-        typecheck(user_id, (int, unicode), user_info, (None, dict))
+        typecheck(user_id, (int, unicode, long), user_info, (None, dict))
         self.user_id = unicode(user_id)
         self.db = db

{"golden_diff": "diff --git a/gittip/elsewhere/__init__.py b/gittip/elsewhere/__init__.py\n--- a/gittip/elsewhere/__init__.py\n+++ b/gittip/elsewhere/__init__.py\n@@ -77,7 +77,7 @@\n         Or:\n         - Takes a user_id and existing_record, and constructs a \"model\" object out of the record\n         \"\"\"\n-        typecheck(user_id, (int, unicode), user_info, (None, dict))\n+        typecheck(user_id, (int, unicode, long), user_info, (None, dict))\n         self.user_id = unicode(user_id)\n         self.db = db\n", "issue": "Sign up with a Twitter account fails.\n```\nInternal server error, program!\nTraceback (most recent call last):\n  File \"/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/algorithm.py\", line 288, in run\n    new_state = function(**deps.as_kwargs)\n  File \"/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/aspen/algorithms/website.py\", line 88, in get_response_for_resource\n    return {'response': resource.respond(request)}\n  File \"/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/aspen/resources/dynamic_resource.py\", line 52, in respond\n    exec self.pages[1] in context\n  File \"/home/sim6/www.gittip.com/www/on/twitter/associate.spt\", line 77, in \n    account = twitter.TwitterAccount(website.db, user_info['id'], user_info)\n  File \"/home/sim6/www.gittip.com/gittip/elsewhere/__init__.py\", line 80, in __init__\n    typecheck(user_id, (int, unicode), user_info, (None, dict))\n  File \"/home/sim6/www.gittip.com/env/local/lib/python2.7/site-packages/aspen/utils.py\", line 377, in typecheck\n    raise TypeError(msg)\nTypeError: Check #1: 2304869623L is of type long, not one of: int, unicode.\n```\n\n", "before_files": [{"content": "\"\"\"This subpackage contains functionality for working with accounts elsewhere.\n\"\"\"\nfrom __future__ import print_function, unicode_literals\nfrom collections import OrderedDict\n\nfrom aspen.utils import typecheck\nfrom aspen import json\nfrom psycopg2 import IntegrityError\n\nimport gittip\nfrom gittip.exceptions import ProblemChangingUsername, UnknownPlatform\nfrom gittip.utils.username import reserve_a_random_username\n\n\nACTIONS = [u'opt-in', u'connect', u'lock', u'unlock']\n\n\n# to add a new elsewhere/platform:\n#  1) add its name (also the name of its module) to this list.\n#     it's best to append it; this ordering is used in templates.\n#  2) inherit from AccountElsewhere in the platform class\n#\n# platform_modules will populate the platform class automatically in configure-aspen.\nplatforms_ordered = (\n    'twitter',\n    'github',\n    'bitbucket',\n    'bountysource',\n    'venmo',\n    'openstreetmap'\n)\n\n# init-time key setup ensures the future ordering of platform_classes will match\n# platforms_ordered, since overwriting entries will maintain their order.\nplatform_classes = OrderedDict([(platform, None) for platform in platforms_ordered])\n\n\nclass _RegisterPlatformMeta(type):\n    \"\"\"Tied to AccountElsewhere to enable registration by the platform field.\n    \"\"\"\n\n    def __new__(cls, name, bases, dct):\n        c = super(_RegisterPlatformMeta, cls).__new__(cls, name, bases, dct)\n\n        # * register the platform\n        # * verify it was added at init-time\n        # * register the subclass's json encoder with aspen\n        c_platform = getattr(c, 'platform')\n        if name == 'AccountElsewhere':\n            pass\n        elif c_platform not in platform_classes:\n            raise UnknownPlatform(c_platform)  # has it been added to platform_classes init?\n        else:\n            platform_classes[c_platform] = c\n\n            # aspen's json encoder registry does not take class hierarchies into account,\n            #  so we need to register the subclasses explicitly.\n            json.register_encoder(c, c.to_json_compatible_object)\n\n        return c\n\nclass AccountElsewhere(object):\n\n    __metaclass__ = _RegisterPlatformMeta\n\n    platform = None  # set in subclass\n\n    # only fields in this set will be encoded\n    json_encode_field_whitelist = set([\n        'id', 'is_locked', 'participant', 'platform', 'user_id', 'user_info',\n    ])\n\n    def __init__(self, db, user_id, user_info=None, existing_record=None):\n        \"\"\"Either:\n        - Takes a user_id and user_info, and updates the database.\n\n        Or:\n        - Takes a user_id and existing_record, and constructs a \"model\" object out of the record\n        \"\"\"\n        typecheck(user_id, (int, unicode), user_info, (None, dict))\n        self.user_id = unicode(user_id)\n        self.db = db\n\n        if user_info is not None:\n            a,b,c,d  = self.upsert(user_info)\n\n            self.participant = a\n            self.is_claimed = b\n            self.is_locked = c\n            self.balance = d\n\n            self.user_info = user_info\n\n        # hack to make this into a weird pseudo-model that can share convenience methods\n        elif existing_record is not None:\n            self.participant = existing_record.participant\n            self.is_claimed, self.is_locked, self.balance = self.get_misc_info(self.participant)\n            self.user_info = existing_record.user_info\n            self.record = existing_record\n\n    def to_json_compatible_object(self):\n        \"\"\"\n        This is registered as an aspen.json encoder in configure-aspen\n        for all subclasses of this class.\n\n        It only exports fields in the whitelist.\n        \"\"\"\n        output = {k: v for (k,v) in self.record._asdict().items()\n                  if k in self.json_encode_field_whitelist}\n\n        return output\n\n    def set_is_locked(self, is_locked):\n        self.db.run(\"\"\"\n\n            UPDATE elsewhere\n               SET is_locked=%s\n             WHERE platform=%s AND user_id=%s\n\n        \"\"\", (is_locked, self.platform, self.user_id))\n\n\n    def opt_in(self, desired_username):\n        \"\"\"Given a desired username, return a User object.\n        \"\"\"\n        from gittip.security.user import User\n\n        self.set_is_locked(False)\n        user = User.from_username(self.participant)\n        user.sign_in()\n        assert not user.ANON, self.participant  # sanity check\n        if self.is_claimed:\n            newly_claimed = False\n        else:\n            newly_claimed = True\n            user.participant.set_as_claimed()\n            try:\n                user.participant.change_username(desired_username)\n            except ProblemChangingUsername:\n                pass\n        return user, newly_claimed\n\n\n    def upsert(self, user_info):\n        \"\"\"Given a dict, return a tuple.\n\n        User_id is an immutable unique identifier for the given user on the\n        given platform.  Username is the user's login/username on the given\n        platform. It is only used here for logging. Specifically, we don't\n        reserve their username for them on Gittip if they're new here. We give\n        them a random username here, and they'll have a chance to change it\n        if/when they opt in. User_id and username may or may not be the same.\n        User_info is a dictionary of profile info per the named platform.  All\n        platform dicts must have an id key that corresponds to the primary key\n        in the underlying table in our own db.\n\n        The return value is a tuple: (username [unicode], is_claimed [boolean],\n        is_locked [boolean], balance [Decimal]).\n\n        \"\"\"\n        typecheck(user_info, dict)\n\n\n        # Insert the account if needed.\n        # =============================\n        # Do this with a transaction so that if the insert fails, the\n        # participant we reserved for them is rolled back as well.\n\n        try:\n            with self.db.get_cursor() as cursor:\n                _username = reserve_a_random_username(cursor)\n                cursor.execute( \"INSERT INTO elsewhere \"\n                                \"(platform, user_id, participant) \"\n                                \"VALUES (%s, %s, %s)\"\n                              , (self.platform, self.user_id, _username)\n                               )\n        except IntegrityError:\n            pass\n\n\n        # Update their user_info.\n        # =======================\n        # Cast everything to unicode, because (I believe) hstore can take any\n        # type of value, but psycopg2 can't.\n        #\n        #   https://postgres.heroku.com/blog/past/2012/3/14/introducing_keyvalue_data_storage_in_heroku_postgres/\n        #   http://initd.org/psycopg/docs/extras.html#hstore-data-type\n        #\n        # XXX This clobbers things, of course, such as booleans. See\n        # /on/bitbucket/%username/index.html\n\n        for k, v in user_info.items():\n            user_info[k] = unicode(v)\n\n\n        username = self.db.one(\"\"\"\n\n            UPDATE elsewhere\n               SET user_info=%s\n             WHERE platform=%s AND user_id=%s\n         RETURNING participant\n\n        \"\"\", (user_info, self.platform, self.user_id))\n\n        return (username,) + self.get_misc_info(username)\n\n    def get_misc_info(self, username):\n        rec = self.db.one(\"\"\"\n\n            SELECT claimed_time, balance, is_locked\n              FROM participants\n              JOIN elsewhere\n                ON participants.username=participant\n             WHERE platform=%s\n               AND participants.username=%s\n\n        \"\"\", (self.platform, username))\n\n        assert rec is not None  # sanity check\n\n        return ( rec.claimed_time is not None\n               , rec.is_locked\n               , rec.balance\n                )\n\n    def set_oauth_tokens(self, access_token, refresh_token, expires):\n        \"\"\"\n        Updates the elsewhere row with the given access token, refresh token, and Python datetime\n        \"\"\"\n\n        self.db.run(\"\"\"\n            UPDATE elsewhere \n            SET (access_token, refresh_token, expires) \n            = (%s, %s, %s) \n            WHERE platform=%s AND user_id=%s\n        \"\"\", (access_token, refresh_token, expires, self.platform, self.user_id))\n", "path": "gittip/elsewhere/__init__.py"}], "after_files": [{"content": "\"\"\"This subpackage contains functionality for working with accounts elsewhere.\n\"\"\"\nfrom __future__ import print_function, unicode_literals\nfrom collections import OrderedDict\n\nfrom aspen.utils import typecheck\nfrom aspen import json\nfrom psycopg2 import IntegrityError\n\nimport gittip\nfrom gittip.exceptions import ProblemChangingUsername, UnknownPlatform\nfrom gittip.utils.username import reserve_a_random_username\n\n\nACTIONS = [u'opt-in', u'connect', u'lock', u'unlock']\n\n\n# to add a new elsewhere/platform:\n#  1) add its name (also the name of its module) to this list.\n#     it's best to append it; this ordering is used in templates.\n#  2) inherit from AccountElsewhere in the platform class\n#\n# platform_modules will populate the platform class automatically in configure-aspen.\nplatforms_ordered = (\n    'twitter',\n    'github',\n    'bitbucket',\n    'bountysource',\n    'venmo',\n    'openstreetmap'\n)\n\n# init-time key setup ensures the future ordering of platform_classes will match\n# platforms_ordered, since overwriting entries will maintain their order.\nplatform_classes = OrderedDict([(platform, None) for platform in platforms_ordered])\n\n\nclass _RegisterPlatformMeta(type):\n    \"\"\"Tied to AccountElsewhere to enable registration by the platform field.\n    \"\"\"\n\n    def __new__(cls, name, bases, dct):\n        c = super(_RegisterPlatformMeta, cls).__new__(cls, name, bases, dct)\n\n        # * register the platform\n        # * verify it was added at init-time\n        # * register the subclass's json encoder with aspen\n        c_platform = getattr(c, 'platform')\n        if name == 'AccountElsewhere':\n            pass\n        elif c_platform not in platform_classes:\n            raise UnknownPlatform(c_platform)  # has it been added to platform_classes init?\n        else:\n            platform_classes[c_platform] = c\n\n            # aspen's json encoder registry does not take class hierarchies into account,\n            #  so we need to register the subclasses explicitly.\n            json.register_encoder(c, c.to_json_compatible_object)\n\n        return c\n\nclass AccountElsewhere(object):\n\n    __metaclass__ = _RegisterPlatformMeta\n\n    platform = None  # set in subclass\n\n    # only fields in this set will be encoded\n    json_encode_field_whitelist = set([\n        'id', 'is_locked', 'participant', 'platform', 'user_id', 'user_info',\n    ])\n\n    def __init__(self, db, user_id, user_info=None, existing_record=None):\n        \"\"\"Either:\n        - Takes a user_id and user_info, and updates the database.\n\n        Or:\n        - Takes a user_id and existing_record, and constructs a \"model\" object out of the record\n        \"\"\"\n        typecheck(user_id, (int, unicode, long), user_info, (None, dict))\n        self.user_id = unicode(user_id)\n        self.db = db\n\n        if user_info is not None:\n            a,b,c,d  = self.upsert(user_info)\n\n            self.participant = a\n            self.is_claimed = b\n            self.is_locked = c\n            self.balance = d\n\n            self.user_info = user_info\n\n        # hack to make this into a weird pseudo-model that can share convenience methods\n        elif existing_record is not None:\n            self.participant = existing_record.participant\n            self.is_claimed, self.is_locked, self.balance = self.get_misc_info(self.participant)\n            self.user_info = existing_record.user_info\n            self.record = existing_record\n\n    def to_json_compatible_object(self):\n        \"\"\"\n        This is registered as an aspen.json encoder in configure-aspen\n        for all subclasses of this class.\n\n        It only exports fields in the whitelist.\n        \"\"\"\n        output = {k: v for (k,v) in self.record._asdict().items()\n                  if k in self.json_encode_field_whitelist}\n\n        return output\n\n    def set_is_locked(self, is_locked):\n        self.db.run(\"\"\"\n\n            UPDATE elsewhere\n               SET is_locked=%s\n             WHERE platform=%s AND user_id=%s\n\n        \"\"\", (is_locked, self.platform, self.user_id))\n\n\n    def opt_in(self, desired_username):\n        \"\"\"Given a desired username, return a User object.\n        \"\"\"\n        from gittip.security.user import User\n\n        self.set_is_locked(False)\n        user = User.from_username(self.participant)\n        user.sign_in()\n        assert not user.ANON, self.participant  # sanity check\n        if self.is_claimed:\n            newly_claimed = False\n        else:\n            newly_claimed = True\n            user.participant.set_as_claimed()\n            try:\n                user.participant.change_username(desired_username)\n            except ProblemChangingUsername:\n                pass\n        return user, newly_claimed\n\n\n    def upsert(self, user_info):\n        \"\"\"Given a dict, return a tuple.\n\n        User_id is an immutable unique identifier for the given user on the\n        given platform.  Username is the user's login/username on the given\n        platform. It is only used here for logging. Specifically, we don't\n        reserve their username for them on Gittip if they're new here. We give\n        them a random username here, and they'll have a chance to change it\n        if/when they opt in. User_id and username may or may not be the same.\n        User_info is a dictionary of profile info per the named platform.  All\n        platform dicts must have an id key that corresponds to the primary key\n        in the underlying table in our own db.\n\n        The return value is a tuple: (username [unicode], is_claimed [boolean],\n        is_locked [boolean], balance [Decimal]).\n\n        \"\"\"\n        typecheck(user_info, dict)\n\n\n        # Insert the account if needed.\n        # =============================\n        # Do this with a transaction so that if the insert fails, the\n        # participant we reserved for them is rolled back as well.\n\n        try:\n            with self.db.get_cursor() as cursor:\n                _username = reserve_a_random_username(cursor)\n                cursor.execute( \"INSERT INTO elsewhere \"\n                                \"(platform, user_id, participant) \"\n                                \"VALUES (%s, %s, %s)\"\n                              , (self.platform, self.user_id, _username)\n                               )\n        except IntegrityError:\n            pass\n\n\n        # Update their user_info.\n        # =======================\n        # Cast everything to unicode, because (I believe) hstore can take any\n        # type of value, but psycopg2 can't.\n        #\n        #   https://postgres.heroku.com/blog/past/2012/3/14/introducing_keyvalue_data_storage_in_heroku_postgres/\n        #   http://initd.org/psycopg/docs/extras.html#hstore-data-type\n        #\n        # XXX This clobbers things, of course, such as booleans. See\n        # /on/bitbucket/%username/index.html\n\n        for k, v in user_info.items():\n            user_info[k] = unicode(v)\n\n\n        username = self.db.one(\"\"\"\n\n            UPDATE elsewhere\n               SET user_info=%s\n             WHERE platform=%s AND user_id=%s\n         RETURNING participant\n\n        \"\"\", (user_info, self.platform, self.user_id))\n\n        return (username,) + self.get_misc_info(username)\n\n    def get_misc_info(self, username):\n        rec = self.db.one(\"\"\"\n\n            SELECT claimed_time, balance, is_locked\n              FROM participants\n              JOIN elsewhere\n                ON participants.username=participant\n             WHERE platform=%s\n               AND participants.username=%s\n\n        \"\"\", (self.platform, username))\n\n        assert rec is not None  # sanity check\n\n        return ( rec.claimed_time is not None\n               , rec.is_locked\n               , rec.balance\n                )\n\n    def set_oauth_tokens(self, access_token, refresh_token, expires):\n        \"\"\"\n        Updates the elsewhere row with the given access token, refresh token, and Python datetime\n        \"\"\"\n\n        self.db.run(\"\"\"\n            UPDATE elsewhere \n            SET (access_token, refresh_token, expires) \n            = (%s, %s, %s) \n            WHERE platform=%s AND user_id=%s\n        \"\"\", (access_token, refresh_token, expires, self.platform, self.user_id))\n", "path": "gittip/elsewhere/__init__.py"}]}