Exemple #1
0
def payday():

    # Wire things up.
    # ===============

    env = wireup.env()
    db = wireup.db(env)

    wireup.billing(env)

    # Lazily import the billing module.
    # =================================
    # This dodges a problem where db in billing is None if we import it from
    # gratipay before calling wireup.billing.

    from gratipay.billing.exchanges import sync_with_balanced
    from gratipay.billing.payday import Payday

    try:
        sync_with_balanced(db)
        Payday.start().run()
    except KeyboardInterrupt:
        pass
    except:
        import aspen
        import traceback
        aspen.log(traceback.format_exc())
Exemple #2
0
def main(db=None, *a, **kw):
    db = db or wireup.db(wireup.env())
    clean_db(db)
    prep_db(db)
    populate_db(db, *a, **kw)
    clean_db(db)
    check_db(db)
Exemple #3
0
def compute_input_csv():
    db = wireup.db(wireup.env())
    participants = db.all("""

        SELECT p.*, r.address AS paypal_email, r.fee_cap AS paypal_fee_cap
          FROM exchange_routes r
          JOIN participants p ON p.id = r.participant
         WHERE r.network = 'paypal'
           AND p.balance > 0
      ORDER BY p.balance DESC

    """)
    writer = csv.writer(open(INPUT_CSV, 'w+'))
    print_rule(88)
    headers = "username", "email", "fee cap", "balance", "tips", "amount"
    print("{:<24}{:<32} {:^7} {:^7} {:^7} {:^7}".format(*headers))
    print_rule(88)
    total_gross = 0
    for participant in participants:
        total = participant.giving + participant.pledging
        amount = participant.balance - total
        if amount < 0.50:
            # Minimum payout of 50 cents. I think that otherwise PayPal upcharges to a penny.
            # See https://github.com/gratipay/gratipay.com/issues/1958.
            continue
        total_gross += amount
        print("{:<24}{:<32} {:>7} {:>7} {:>7} {:>7}".format(
            participant.username, participant.paypal_email,
            participant.paypal_fee_cap, participant.balance, total, amount))
        row = (participant.username, participant.paypal_email,
               participant.paypal_fee_cap, amount)
        writer.writerow(row)
    print(" " * 80, "-" * 7)
    print("{:>88}".format(total_gross))
Exemple #4
0
def compute_input_csv():
    from gratipay import wireup
    db = wireup.db(wireup.env())
    participants = db.all("""

        SELECT participants.*::participants
          FROM participants
         WHERE paypal_email IS NOT null
           AND balance > 0
      ORDER BY balance DESC

    """)
    writer = csv.writer(open(INPUT_CSV, 'w+'))
    print_rule(88)
    headers = "username", "email", "fee cap", "balance", "tips", "amount"
    print("{:<24}{:<32} {:^7} {:^7} {:^7} {:^7}".format(*headers))
    print_rule(88)
    total_gross = 0
    for participant in participants:
        total = participant.giving + participant.pledging
        amount = participant.balance - total
        if amount < 0.50:
            # Minimum payout of 50 cents. I think that otherwise PayPal upcharges to a penny.
            # See https://github.com/gratipay/gratipay.com/issues/1958.
            continue
        total_gross += amount
        print("{:<24}{:<32} {:>7} {:>7} {:>7} {:>7}".format(
            participant.username, participant.paypal_email,
            participant.paypal_fee_cap, participant.balance, total, amount))
        row = (participant.username, participant.paypal_email,
               participant.paypal_fee_cap, amount)
        writer.writerow(row)
    print(" " * 80, "-" * 7)
    print("{:>88}".format(total_gross))
Exemple #5
0
def main():
    """This function is installed via an entrypoint in ``setup.py`` as
    ``sync-npm``.

    Usage::

      sync-npm

    """
    env = wireup.env()
    db = wireup.db(env)
    while 1:
        with sentry.teller(env):
            last_seq = get_last_seq(db)
            log("Picking up with npm sync at {}.".format(last_seq))
            stream = production_change_stream(last_seq)
            consume_change_stream(stream, db)
        try:
            last_seq = get_last_seq(db)
            sleep_for = 60
            log('Encountered an error, will pick up with %s in %s seconds (Ctrl-C to exit) ...'
                % (last_seq, sleep_for))
            time.sleep(sleep_for)  # avoid a busy loop if thrashing
        except KeyboardInterrupt:
            return
Exemple #6
0
def upsert(args):
    from gratipay import wireup
    db = wireup.db(wireup.env())
    fp = open(args.path)
    with db.get_cursor() as cursor:
        assert cursor.connection.encoding == 'UTF8'

        # http://tapoueh.org/blog/2013/03/15-batch-update.html
        cursor.run(
            "CREATE TEMP TABLE updates (LIKE packages INCLUDING ALL) ON COMMIT DROP"
        )
        cursor.copy_expert(
            'COPY updates (package_manager, name, description, emails) '
            "FROM STDIN WITH (FORMAT csv, NULL '%s')" % NULL, fp)
        cursor.run("""

            WITH updated AS (
                UPDATE packages p
                   SET package_manager = u.package_manager
                     , description = u.description
                     , emails = u.emails
                  FROM updates u
                 WHERE p.name = u.name
             RETURNING p.name
            )
            INSERT INTO packages(package_manager, name, description, emails)
                 SELECT package_manager, name, description, emails
                   FROM updates u LEFT JOIN updated USING(name)
                  WHERE updated.name IS NULL
               GROUP BY u.package_manager, u.name, u.description, u.emails

        """)
def main(db=None, *a, **kw):
    db = db or wireup.db(wireup.env())
    clean_db(db)
    prep_db(db)
    populate_db(db, *a, **kw)
    clean_db(db)
    check_db(db)
Exemple #8
0
def payday():

    # Wire things up.
    # ===============

    env = wireup.env()
    db = wireup.db(env)

    wireup.billing(env)
    wireup.nanswers(env)

    # Lazily import the billing module.
    # =================================
    # This dodges a problem where db in billing is None if we import it from
    # gratipay before calling wireup.billing.

    from gratipay.billing.exchanges import sync_with_balanced
    from gratipay.billing.payday import Payday

    try:
        sync_with_balanced(db)
        Payday.start().run()
    except KeyboardInterrupt:
        pass
    except:
        import aspen
        import traceback

        aspen.log(traceback.format_exc())
Exemple #9
0
def compute_input_csv():
    db = wireup.db(wireup.env())
    routes = get_ready_payout_routes_by_network(db, 'paypal')
    writer = csv.writer(open(INPUT_CSV, 'w+'))
    print_rule(88)
    headers = "username", "email", "fee cap", "amount"
    print("{:<24}{:<32} {:^7} {:^7}".format(*headers))
    print_rule(88)
    total_gross = 0
    for route in routes:
        amount = route.participant.balance
        if amount < 0.50:
            # Minimum payout of 50 cents. I think that otherwise PayPal upcharges to a penny.
            # See https://github.com/gratipay/gratipay.com/issues/1958.
            continue
        total_gross += amount
        print("{:<24}{:<32} {:>7} {:>7}".format( route.participant.username
                                                           , route.address
                                                           , route.fee_cap
                                                           , amount
                                                            ))
        row = (route.participant.username, route.address, route.fee_cap, amount)
        writer.writerow(row)
    print(" "*80, "-"*7)
    print("{:>88}".format(total_gross))
Exemple #10
0
def main(argv=sys.argv):
    """This function is installed via an entrypoint in ``setup.py`` as
    ``sync-npm``.

    Usage::

      sync-npm {serialize,upsert,fetch-readmes,process-readmes} {<filepath>}

    ``<filepath>`` defaults to stdin. It's necessary for ``serialize`` and
    ``upsert``, and silently ignored for ``{fetch,process}-readmes``.

    .. note:: Sphinx is expanding ``sys.argv`` in the parameter list. Sorry. :-/

    """
    env = wireup.env()
    args = parse_args(argv[1:])
    db = wireup.db(env)

    try:
        sys.stdout = sys.stderr  # work around aspen.log_dammit limitation; sigh
        tell_sentry = wireup.make_sentry_teller(env)
    finally:
        sys.stdout = sys.__stdout__

    def sentrified(func):
        def _(*a, **kw):
            try:
                func(*a, **kw)
            except:
                e = sys.exc_info()[0]
                tell_sentry(e, {})

        return _

    subcommands[args.command](env, args, db, sentrified)
def compute_input_csv():
    db = wireup.db(wireup.env())
    routes = get_ready_payout_routes_by_network(db, 'paypal')
    writer = csv.writer(open(INPUT_CSV, 'w+'))
    print_rule(88)
    headers = "username", "email", "fee cap", "amount"
    print("{:<24}{:<32} {:^7} {:^7}".format(*headers))
    print_rule(88)
    total_gross = 0
    for route in routes:
        amount = route.participant.balance
        if amount < 0.50:
            # Minimum payout of 50 cents. I think that otherwise PayPal upcharges to a penny.
            # See https://github.com/gratipay/gratipay.com/issues/1958.
            continue
        total_gross += amount
        print("{:<24}{:<32} {:>7} {:>7}".format( route.participant.username
                                                           , route.address
                                                           , route.fee_cap
                                                           , amount
                                                            ))
        row = (route.participant.username, route.address, route.fee_cap, amount)
        writer.writerow(row)
    print(" "*64, "-"*7)
    print("{:>72}".format(total_gross))
Exemple #12
0
    def setUp(self):
        Harness.setUp(self)

        # Grab configuration from the environment, storing for later.
        env = wireup.env()
        self.environ = env.environ

        # Change env, doesn't change self.environ.
        env.canonical_scheme = 'https'
        env.canonical_host = 'gratipay.com'

        wireup.canonical(env)
def main(_argv=sys.argv, _print=print):
    """This is a script to list the email queue.
    """
    env = wireup.env()

    db = wireup.db(env)
    for i, email in enumerate(db.all(QUEUE), start=1):
        _print("{:>4} {:>6} {:<16} {:<24} {}".format(i, *email))

    # Trigger logging of sentry/mailer configuration for reference.
    wireup.make_sentry_teller(env)
    wireup.mail(env)
def main(_argv=sys.argv, _print=print):
    """This is a script to list the email queue.
    """
    env = wireup.env()

    db = wireup.db(env)
    for i, email in enumerate(db.all(QUEUE), start=1):
        _print("{:>4} {:>6} {:<16} {:<24} {}".format(i, *email))

    # Trigger logging of sentry/mailer configuration for reference.
    wireup.make_sentry_teller(env)
    wireup.mail(env)
Exemple #15
0
    def setUp(self):
        Harness.setUp(self)

        # Grab configuration from the environment, storing for later.
        env = wireup.env()
        self.environ = env.environ

        # Change env, doesn't change self.environ.
        env.canonical_scheme = 'https'
        env.canonical_host = 'gratipay.com'

        wireup.canonical(env)
Exemple #16
0
def set_paypal_email(username='',
                     email='',
                     api_key_fragment='',
                     overwrite=False):
    """
    Usage:

    [gratipay] $ env/bin/invoke set_paypal_email --username=username [email protected] [--api-key-fragment=12e4s678] [--overwrite]
    """

    if not username or not email:
        print_help(set_paypal_email)
        sys.exit(1)

    if not os.environ.get('DATABASE_URL'):
        load_prod_envvars()

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    wireup.db(wireup.env())

    participant = Participant.from_username(username)
    if not participant:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    route = ExchangeRoute.from_network(participant, 'paypal')

    # PayPal caps the MassPay fee at $20 for users outside the U.S., and $1 for
    # users inside the U.S. Most Gratipay users using PayPal are outside the U.S.
    # so we set to $20 and I'll manually adjust to $1 when running MassPay and
    # noticing that something is off.
    FEE_CAP = 20

    if route:
        print("PayPal email is already set to: " + route.address)
        if not overwrite:
            print("Not overwriting existing PayPal email.")
            sys.exit(3)

    if participant.api_key == None:
        assert first_eight == "None"
    else:
        assert participant.api_key[0:8] == first_eight

    print("Setting PayPal email for " + username + " to " + email)
    ExchangeRoute.insert(participant, 'paypal', email, fee_cap=FEE_CAP)
    print("All done.")
def main(_argv=sys.argv, _input=raw_input):
    """This is a script to dequeue and send emails.

    In production we have a thread inside the main web process that dequeues
    emails according to the DEQUEUE_EMAILS_EVERY envvar. This script is more
    for development, though when we're ready to move to a separate worker
    process/dyno we can start with this.

    """
    env = wireup.env()
    wireup.make_sentry_teller(env)
    wireup.mail(env)
    wireup.db(env)
    Participant.dequeue_emails()
Exemple #18
0
def set_paypal_email(username='', email='', api_key_fragment='', overwrite=False):
    """
    Usage:

    [gratipay] $ env/bin/invoke set_paypal_email --username=username [email protected] [--api-key-fragment=12e4s678] [--overwrite]
    """

    if not username or not email:
        print_help(set_paypal_email)
        sys.exit(1)

    if not os.environ.get('DATABASE_URL'):
        load_prod_envvars()

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    wireup.db(wireup.env())

    participant = Participant.from_username(username)
    if not participant:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    route = ExchangeRoute.from_network(participant, 'paypal')

    # PayPal caps the MassPay fee at $20 for users outside the U.S., and $1 for
    # users inside the U.S. Most Gratipay users using PayPal are outside the U.S.
    # so we set to $20 and I'll manually adjust to $1 when running MassPay and
    # noticing that something is off.
    FEE_CAP = 20

    if route:
        print("PayPal email is already set to: " + route.address)
        if not overwrite:
            print("Not overwriting existing PayPal email.")
            sys.exit(3)

    if participant.api_key == None:
        assert first_eight == "None"
    else:
        assert participant.api_key[0:8] == first_eight

    print("Setting PayPal email for " + username + " to " + email)
    ExchangeRoute.insert(participant, 'paypal', email, fee_cap=FEE_CAP)
    print("All done.")
Exemple #19
0
def compute_input_csv():
    db = wireup.db(wireup.env())
    participants = db.all("""

        SELECT p.*, r.address AS paypal_email, r.fee_cap AS paypal_fee_cap
          FROM exchange_routes r
          JOIN participants p ON p.id = r.participant
         WHERE r.network = 'paypal'
           AND p.balance > 0

          ---- Only include team owners
          ---- TODO: Include members on payroll once process_payroll is implemented

               AND ( SELECT count(*)
                       FROM teams t
                      WHERE t.owner = p.username
                        AND t.is_approved IS TRUE
                        AND t.is_closed IS NOT TRUE
                   ) > 0

      ORDER BY p.balance DESC

    """)
    writer = csv.writer(open(INPUT_CSV, 'w+'))
    print_rule(88)
    headers = "username", "email", "fee cap", "balance", "tips", "amount"
    print("{:<24}{:<32} {:^7} {:^7} {:^7} {:^7}".format(*headers))
    print_rule(88)
    total_gross = 0
    for participant in participants:
        total = participant.giving
        amount = participant.balance - total
        if amount < 0.50:
            # Minimum payout of 50 cents. I think that otherwise PayPal upcharges to a penny.
            # See https://github.com/gratipay/gratipay.com/issues/1958.
            continue
        total_gross += amount
        print("{:<24}{:<32} {:>7} {:>7} {:>7} {:>7}".format( participant.username
                                                           , participant.paypal_email
                                                           , participant.paypal_fee_cap
                                                           , participant.balance
                                                           , total
                                                           , amount
                                                            ))
        row = (participant.username, participant.paypal_email, participant.paypal_fee_cap, amount)
        writer.writerow(row)
    print(" "*80, "-"*7)
    print("{:>88}".format(total_gross))
    def test_stats_description_accurate_during_payday_run(self, utcnow):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gratipay/gratipay.com/issues/92.
        """
        a_thursday = DateTime(2012, 8, 9, 11, 00, 01)
        utcnow.return_value = a_thursday

        self.client.hydrate_website()

        env = wireup.env()
        wireup.billing(env)
        payday = Payday.start()

        body = self.get_stats_page()
        assert "is changing hands <b>right now!</b>" in body, body
        payday.end()
Exemple #21
0
def main(argv=sys.argv):
    """This function is installed via an entrypoint in ``setup.py`` as
    ``sync-npm``.

    Usage::

      sync-npm {serialize,upsert} {<filepath>}

    ``<filepath>`` defaults to stdin.

    .. note:: Sphinx is expanding ``sys.argv`` in the parameter list. Sorry. :-/

    """
    env = wireup.env()
    args = parse_args(argv[1:])
    db = wireup.db(env)

    subcommands[args.command](env, args, db)
Exemple #22
0
def main(argv=sys.argv):
    """This function is installed via an entrypoint in ``setup.py`` as
    ``sync-npm``.

    Usage::

      sync-npm {serialize,upsert} {<filepath>}

    ``<filepath>`` defaults to stdin.

    .. note:: Sphinx is expanding ``sys.argv`` in the parameter list. Sorry. :-/

    """
    env = wireup.env()
    args = parse_args(argv[1:])
    db = wireup.db(env)

    subcommands[args.command](env, args, db)
Exemple #23
0
    def test_stats_description_accurate_during_payday_run(self, utcnow):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gratipay/gratipay.com/issues/92.
        """
        a_thursday = DateTime(2012, 8, 9, 11, 00, 01)
        utcnow.return_value = a_thursday

        self.client.hydrate_website()

        env = wireup.env()
        wireup.billing(env)
        payday = Payday.start()

        body = self.get_stats_page()
        assert "is changing hands <b>right now!</b>" in body, body
        payday.end()
Exemple #24
0
def compute_input_csv():
    from gratipay import wireup
    db = wireup.db(wireup.env())
    participants = db.all("""

        SELECT participants.*::participants
          FROM participants
         WHERE paypal_email IS NOT null
           AND balance > 0
      ORDER BY balance DESC

    """)
    writer = csv.writer(open(INPUT_CSV, 'w+'))
    print_rule(88)
    headers = "username", "email", "fee cap", "balance", "tips", "amount"
    print("{:<24}{:<32} {:^7} {:^7} {:^7} {:^7}".format(*headers))
    print_rule(88)
    total_gross = 0
    for participant in participants:
        total = participant.giving + participant.pledging
        amount = participant.balance - total
        if amount < 0.50:
            # Minimum payout of 50 cents. I think that otherwise PayPal upcharges to a penny.
            # See https://github.com/gratipay/gratipay.com/issues/1958.
            continue
        total_gross += amount
        print("{:<24}{:<32} {:>7} {:>7} {:>7} {:>7}".format( participant.username
                                                           , participant.paypal_email
                                                           , participant.paypal_fee_cap
                                                           , participant.balance
                                                           , total
                                                           , amount
                                                            ))
        row = (participant.username, participant.paypal_email, participant.paypal_fee_cap, amount)
        writer.writerow(row)
    print(" "*80, "-"*7)
    print("{:>88}".format(total_gross))
Exemple #25
0
#!/usr/bin/env python
"""verify-identity.py <participant_id>, <country_code>
"""
from __future__ import absolute_import, division, print_function, unicode_literals

import sys

from gratipay import wireup
from gratipay.models.participant import Participant
from gratipay.models.country import Country

wireup.db(wireup.env())

participant = Participant.from_id(int(sys.argv[1]))
country = Country.from_code(sys.argv[2])
participant.set_identity_verification(country.id, True)
Exemple #26
0
def main():
    db = wireup.db(wireup.env())
    prep_db(db)
    populate_db(db)
    clean_db(db)
    check_db(db)
Exemple #27
0
def set_paypal_email(username='', email='', api_key_fragment='', overwrite=False):
    """
    Usage:

    [gratipay] $ env/bin/invoke set_paypal_email --username=username [email protected] [--api-key-fragment=12e4s678] [--overwrite]
    """

    if not os.environ.get('DATABASE_URL'):
        load_prod_envvars()

    if not username or not email:
        print(set_paypal_email.__doc__)
        sys.exit(1)

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    db = wireup.db(wireup.env())

    FIELDS = """
            SELECT username, api_key, paypal_email
              FROM participants
             WHERE username = %s
    """

    fields = db.one(FIELDS, (username,))

    print(fields)

    if fields == None:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    # PayPal caps the MassPay fee at $20 for users outside the U.S., and $1 for
    # users inside the U.S. Most Gratipay users using PayPal are outside the U.S.
    # so we set to $20 and I'll manually adjust to $1 when running MassPay and
    # noticing that something is off.
    FEE_CAP = ', paypal_fee_cap=20'

    if fields.paypal_email != None:
        print("PayPal email is already set to: " + fields.paypal_email)
        if not overwrite:
            print("Not overwriting existing PayPal email.")
            sys.exit(3)
        else:
            FEE_CAP = ''  # Don't overwrite fee_cap when overwriting email address.

    if fields.api_key == None:
        assert first_eight == "None"
    else:
        assert fields.api_key[0:8] == first_eight

    print("Setting PayPal email for " + username + " to " + email)

    SET_EMAIL = """
            UPDATE participants
               SET paypal_email=%s{}
             WHERE username=%s;
    """.format(FEE_CAP)
    print(SET_EMAIL % (email, username))

    db.run(SET_EMAIL, (email, username))

    print("All done.")
Exemple #28
0
def bitcoin_payout(username='', amount='', api_key_fragment=''):
    """
    Usage:

    [gratipay] $ env/bin/invoke bitcoin_payout --username=username --amount=amount [--api-key-fragment=12e4s678]
    """

    if not username or not amount:
        print(bitcoin_payout.__doc__)
        sys.exit(1)

    amount = D(amount)
    assert amount >= MINIMUM_COINBASE_PAYOUT
    amount = subtract_fee(amount)

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    db = wireup.db(wireup.env())

    FIELDS = """
            SELECT username, api_key, bitcoin_address, balance
              FROM participants
             WHERE username = %s
    """

    fields = db.one(FIELDS, (username,))

    print(fields)

    if fields == None:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    if not fields.bitcoin_address:
        print(username + " hasn't linked a bitcoin address to their profile!")
        sys.exit(3)
    print("Fetching bitcoin_address from database: " + fields.bitcoin_address)
    bitcoin_address = fields.bitcoin_address

    if D(fields.balance) < D(amount):
        print("Not enough balance. %s only has %f in their account!" % username, D(amount))
        sys.exit(4)

    if fields.api_key == None:
        assert first_eight == "None"
    else:
        assert fields.api_key[0:8] == first_eight

    print("Sending bitcoin payout for " + username + " to " + bitcoin_address)
    try:
        data = {
            "transaction":{
                "to": bitcoin_address,
                "amount_string": str(amount),
                "amount_currency_iso": "USD",
                "notes": "Gratipay Bitcoin Payout",
                "instant_buy": True
            }
        }
        result = coinbase_request('https://api.coinbase.com/v1/transactions/send_money', json.dumps(data))

    except requests.HTTPError as e:
        print(e)
        return e

    if result.status_code != 200:
        print("Oops! Coinbase returned a " + str(result.status_code))
        print(result.json())
        sys.exit(5)
    elif result.json()['success'] != True:
        print("Coinbase transaction didn't succeed!")
        print(result.json())
        sys.exit(6)
    else:
        print("Coinbase transaction succeeded!")
        print("Entering Exchange in database")

        # Get the fee from the response
        fee_dict = result.json()['transfer']['fees']
        assert fee_dict['coinbase']['currency_iso'] == fee_dict['bank']['currency_iso'] == "USD"
        coinbase_fee = int(fee_dict['coinbase']['cents'])
        bank_fee = int(fee_dict['bank']['cents'])
        fee = (coinbase_fee + bank_fee) * D('0.01')

        # Get the amount from the response
        assert result.json()['transfer']['subtotal']['currency'] == "USD"
        amount = -D(result.json()['transfer']['subtotal']['amount']) # Negative amount for payouts
        btcamount = result.json()['transfer']['btc']['amount']

        note = "Sent %s btc to %s" % (btcamount, bitcoin_address)

        with db.get_cursor() as cursor:
            exchange_id = cursor.one("""
                INSERT INTO exchanges
                       (amount, fee, participant, note, status)
                VALUES (%s, %s, %s, %s, %s)
             RETURNING id
            """, (amount, fee, username, note, 'succeeded'))
            new_balance = cursor.one("""
                UPDATE participants
                   SET balance=(balance + %s)
                 WHERE username=%s
             RETURNING balance
            """, (amount - fee, username))
            if new_balance < 0:
                raise NegativeBalance
            print("Exchange recorded: " + str(exchange_id))
            print("New Balance: " + str(new_balance))

    print("All done.")
Exemple #29
0
from datetime import timedelta

from aspen.utils import utcnow

import gratipay

from gratipay import wireup
from gratipay.models.participant import Participant


if len(sys.argv) < 2:
    sys.exit('Usage: %s <user>' % sys.argv[0])


db = Participant.db = wireup.db(wireup.env())
gratipay.RESTRICTED_USERNAMES = os.listdir('./www/')

username = sys.argv[1]
session_token = uuid.uuid4().hex
session_expires = utcnow() + timedelta(hours=6)

participant = Participant.from_username(username)
if not participant:
    participant = Participant.with_random_username()
    participant.change_username(username)
    db.run("""
        INSERT INTO elsewhere
                    (platform, user_id, user_name, participant)
             VALUES ('twitter', %s, %s, %s)
    """, (participant.id, username, username))
Exemple #30
0
def bitcoin_payout(username='', amount='', api_key_fragment=''):
    """
    Usage:

    [gratipay] $ env/bin/invoke bitcoin_payout --username=username --amount=amount [--api-key-fragment=12e4s678]
    """

    if not username or not amount:
        print_help(bitcoin_payout)
        sys.exit(1)

    if not os.environ.get('DATABASE_URL'):
        load_prod_envvars()

    amount = D(amount)
    assert amount >= MINIMUM_COINBASE_PAYOUT
    amount = subtract_fee(amount)

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    db = wireup.db(wireup.env())

    participant = Participant.from_username(username)
    if not participant:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    route = ExchangeRoute.from_network(participant, 'bitcoin')
    if not route:
        print(username + " hasn't linked a bitcoin address to their profile!")
        sys.exit(3)

    bitcoin_address = route.address
    print("Fetched bitcoin_address from database: " + bitcoin_address)

    if D(participant.balance) < D(amount):
        print("Not enough balance. %s only has %f in their account!" %
              (username, D(amount)))
        sys.exit(4)

    if participant.api_key == None:
        assert first_eight == "None"
    else:
        assert participant.api_key[0:8] == first_eight

    print("Sending bitcoin payout for " + username + " to " + bitcoin_address)
    try:
        data = {
            "transaction": {
                "to": bitcoin_address,
                "amount_string": str(amount),
                "amount_currency_iso": "USD",
                "notes": "Gratipay Bitcoin Payout",
                "instant_buy": True
            }
        }
        result = coinbase_request(
            'https://api.coinbase.com/v1/transactions/send_money',
            json.dumps(data))

    except requests.HTTPError as e:
        print(e)
        return e

    if result.status_code != 200:
        print("Oops! Coinbase returned a " + str(result.status_code))
        print(result.json())
        sys.exit(5)
    elif result.json()['success'] != True:
        print("Coinbase transaction didn't succeed!")
        print(result.json())
        sys.exit(6)
    else:
        print("Coinbase transaction succeeded!")
        print("Entering Exchange in database")

        # Get the fee from the response
        fee_dict = result.json()['transfer']['fees']
        assert fee_dict['coinbase']['currency_iso'] == fee_dict['bank'][
            'currency_iso'] == "USD"
        coinbase_fee = int(fee_dict['coinbase']['cents'])
        bank_fee = int(fee_dict['bank']['cents'])
        fee = (coinbase_fee + bank_fee) * D('0.01')

        # Get the amount from the response
        assert result.json()['transfer']['subtotal']['currency'] == "USD"
        amount = -D(result.json()['transfer']['subtotal']
                    ['amount'])  # Negative amount for payouts
        btcamount = result.json()['transfer']['btc']['amount']

        note = "Sent %s btc to %s" % (btcamount, bitcoin_address)

        with db.get_cursor() as cursor:
            exchange_id = cursor.one(
                """
                INSERT INTO exchanges
                       (amount, fee, participant, note, status, route)
                VALUES (%s, %s, %s, %s, %s, %s)
             RETURNING id
            """, (amount, fee, username, note, 'succeeded', route.id))
            new_balance = cursor.one(
                """
                UPDATE participants
                   SET balance=(balance + %s)
                 WHERE username=%s
             RETURNING balance
            """, (amount - fee, username))
            if new_balance < 0:
                raise NegativeBalance
            print("Exchange recorded: " + str(exchange_id))
            print("New Balance: " + str(new_balance))

    print("All done.")
Exemple #31
0
from gratipay.wireup import db, env
from gratipay.models.team import AlreadyMigrated

db = db(env())

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 team in teams:
    try:
        ntips = team.migrate_tips()
        print("Migrated {} tip(s) for '{}'".format(ntips, team.slug))
    except AlreadyMigrated:
        print("'%s' already migrated." % team.slug)

print("Done.")
def main(_argv=sys.argv, _input=raw_input, _print=print):
    """This is a script to enqueue global site notification emails.

    It should only be used for important transactional messages like a Terms of
    Service change. Process:

        - write your email in emails/branch.spt
        - test locally using your own database and AWS keys in local.env
        - when reviewed and merged:
            - deploy
            - `heroku run bash`
            - `queue-branch-email your_username`      # final test from production
            - `queue-branch-email all 2> queued.log`  # !!!
            - make a commit to master to empty branch.spt
              (leave an empty file [but with speclines] or tests will fail)
            - push to GitHub

    """
    db = wireup.db(wireup.env())

    def prompt(msg):
        answer = _input(msg + " [y/N]")
        if answer.lower() != 'y':
            raise SystemExit(1)


    # Fetch participants.
    # ===================

    try:
        username = _argv[1]
    except IndexError:
        _print("Usage: {0} [username|all]".format(_argv[0]))
        raise SystemExit

    if username == 'all':
        prompt("Are you ready?")
        prompt("Are you REALLY ready?")
        prompt("... ?")
        _print("Okay, you asked for it!")
        participants = db.all("""
            SELECT p.*::participants
              FROM participants p
             WHERE email_address is not null
               AND claimed_time  is not null
               AND is_closed     is not true
               AND is_suspicious is not true
        """)
    else:
        _print("Okay, just {}.".format(username))
        participants = db.all("SELECT p.*::participants FROM participants p "
                              "WHERE p.username=%s", (username,))

    N = len(participants)
    _print(N)
    for p in random.sample(participants, 5 if N > 5 else 1) if N else []:
        _print("spotcheck: {} ({}={})".format(p.email_address, p.username, p.id))


    # Send emails.
    # ============

    if username == 'all':
        prompt("But really actually tho? I mean, ... seriously?")

    for i, p in enumerate(participants, start=1):
        _print( "{:>4} queuing for {} ({}={})".format(i, p.email_address, p.username, p.id)
              , file=sys.stderr
               )
        p.queue_email('branch', include_unsubscribe=False)
Exemple #33
0
def main():
    populate_db(wireup.db(wireup.env()))
#!/usr/bin/env python

"""This is a one-off script to resend emails for #3355."""

import sys

from gratipay import wireup

env = wireup.env()
db = wireup.db(env)

# Temporary, will fill with actual values when running script
email_txt = """
    [email protected]
    [email protected]
"""

emails = [email.strip() for email in email_txt.split()]

assert len(emails) == 176

participants = []

participants = db.all("""
    SELECT p.*::participants
      FROM participants p
     WHERE email_address IN %s
""", (tuple(emails), ))

for p in participants:
    p.queue_email('double_emails')
Exemple #35
0
def readmes(args):
    from gratipay import wireup
    db = wireup.db(wireup.env())
    _readmes.sync_all(db)
def main(_argv=sys.argv, _input=raw_input, _print=print):
    """This is a script to enqueue global site notification emails.

    It should only be used for important transactional messages like a Terms of
    Service change. Process:

        - write your email in emails/branch.spt
        - test locally using your own database and AWS keys in local.env
        - when reviewed and merged:
            - deploy
            - `heroku run bash`
            - `queue-branch-email your_username`      # final test from production
            - `queue-branch-email all 2> queued.log`  # !!!
            - make a commit to master to empty branch.spt
              (leave an empty file [but with speclines] or tests will fail)
            - push to GitHub

    """
    db = wireup.db(wireup.env())

    def prompt(msg):
        answer = _input(msg + " [y/N]")
        if answer.lower() != 'y':
            raise SystemExit(1)

    # Fetch participants.
    # ===================

    try:
        username = _argv[1]
    except IndexError:
        _print("Usage: {0} [username|all]".format(_argv[0]))
        raise SystemExit

    if username == 'all':
        prompt("Are you ready?")
        prompt("Are you REALLY ready?")
        prompt("... ?")
        _print("Okay, you asked for it!")
        participants = get_recently_active_participants(db)
    else:
        _print("Okay, just {}.".format(username))
        participants = db.all(
            "SELECT p.*::participants FROM participants p "
            "WHERE p.username=%s", (username, ))

    N = len(participants)
    _print(N)
    for p in random.sample(participants, 5 if N > 5 else 1) if N else []:
        _print("spotcheck: {} ({}={})".format(p.email_address, p.username,
                                              p.id))

    # Send emails.
    # ============

    if username == 'all':
        prompt("But really actually tho? I mean, ... seriously?")

    for i, p in enumerate(participants, start=1):
        _print("{:>4} queuing for {} ({}={})".format(i, p.email_address,
                                                     p.username, p.id),
               file=sys.stderr)
        p.queue_email('branch', include_unsubscribe=False)
Exemple #37
0
def _wireup():
    env = wireup.env()
    db = wireup.db(env)
    wireup.crypto(env)
    return db
def main():
    db = wireup.db(wireup.env())
    prep_db(db)
    populate_db(db)
    clean_db(db)
    check_db(db)
#!/usr/bin/env python -u
from __future__ import absolute_import, division, print_function, unicode_literals

import json, os, sys, traceback
from collections import defaultdict
from gratipay import wireup
from decimal import Decimal as D
from pprint import pprint
from gratipay.models.exchange_route import ExchangeRoute

db = wireup.db(wireup.env())
dirname = os.path.join(os.path.dirname(__file__), 'balanced', 'transactions')


class UnknownExchange(Exception):
    def __str__(self):
        return "\n\n{}".format(self.args[0])

class UnknownCustomer(Exception): pass
class UnknownRoute(Exception): pass
class AmbiguousCustomer(Exception): pass
class AmbiguousRoute(Exception): pass


class CountsOff(Exception):
    def __str__(self):
        msg, counts = self.args
        out = [msg]
        cur = ''
        for name, val in sorted(counts.__dict__.items()):
            if name[0] != cur:
Exemple #40
0
def _wireup():
    env = wireup.env()
    db = wireup.db(env)
    wireup.crypto(env)
    return db
Exemple #41
0
def set_paypal_email(username='',
                     email='',
                     api_key_fragment='',
                     overwrite=False):
    """
    Usage:

    [gratipay] $ env/bin/invoke set_paypal_email --username=username [email protected] [--api-key-fragment=12e4s678] [--overwrite]
    """

    if not os.environ.get('DATABASE_URL'):
        load_prod_envvars()

    if not username or not email:
        print(set_paypal_email.__doc__)
        sys.exit(1)

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    db = wireup.db(wireup.env())

    FIELDS = """
            SELECT username, api_key, paypal_email
              FROM participants
             WHERE username = %s
    """

    fields = db.one(FIELDS, (username, ))

    print(fields)

    if fields == None:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    # PayPal caps the MassPay fee at $20 for users outside the U.S., and $1 for
    # users inside the U.S. Most Gratipay users using PayPal are outside the U.S.
    # so we set to $20 and I'll manually adjust to $1 when running MassPay and
    # noticing that something is off.
    FEE_CAP = ', paypal_fee_cap=20'

    if fields.paypal_email != None:
        print("PayPal email is already set to: " + fields.paypal_email)
        if not overwrite:
            print("Not overwriting existing PayPal email.")
            sys.exit(3)
        else:
            FEE_CAP = ''  # Don't overwrite fee_cap when overwriting email address.

    if fields.api_key == None:
        assert first_eight == "None"
    else:
        assert fields.api_key[0:8] == first_eight

    print("Setting PayPal email for " + username + " to " + email)

    SET_EMAIL = """
            UPDATE participants
               SET paypal_email=%s{}
             WHERE username=%s;
    """.format(FEE_CAP)
    print(SET_EMAIL % (email, username))

    db.run(SET_EMAIL, (email, username))

    print("All done.")
Exemple #42
0
def main():
    populate_db(wireup.db(wireup.env()))
Exemple #43
0
#!/usr/bin/env python2
"""See gratipay.models.participant.mixins.identity.rekey for documentation.
"""
from __future__ import absolute_import, division, print_function, unicode_literals

from gratipay import wireup
from gratipay.models.participant.mixins import identity as participant_identities

env = wireup.env()
db = wireup.db(env)
packer = wireup.crypto(env)

n = participant_identities.rekey(db, packer)
print("Rekeyed {} participant identity record(s).".format(n))