Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
Merge pull request #4495 from gratipay/bulk-prefactor
Browse files Browse the repository at this point in the history
Refactor ahead of bulk npm
  • Loading branch information
rohitpaulk authored Jun 2, 2017
2 parents 6906147 + c74a413 commit 8e743ea
Show file tree
Hide file tree
Showing 8 changed files with 4,887 additions and 2,824 deletions.
6 changes: 6 additions & 0 deletions gratipay/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ def render(t, context):
return message


def purge(self):
"""Remove all messages from the queue.
"""
self.db.run('DELETE FROM email_queue')


jinja_env = Environment()
jinja_env_html = Environment(autoescape=True, extensions=['jinja2.ext.autoescape'])

Expand Down
5 changes: 3 additions & 2 deletions gratipay/models/participant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@
from gratipay.utils.username import safely_reserve_a_username

from .email import Email
from .identity import Identity
from .exchange_routes import ExchangeRoutes
from .identity import Identity
from .packages import Packages

MAX_TIP = MAX_PAYMENT = Decimal('1000.00')
MIN_TIP = MIN_PAYMENT = Decimal('0.00')
Expand All @@ -48,7 +49,7 @@
USERNAME_MAX_SIZE = 32


class Participant(Model, Email, Identity, ExchangeRoutes):
class Participant(Model, Email, ExchangeRoutes, Identity, Packages):
"""Represent a Gratipay participant.
"""

Expand Down
66 changes: 1 addition & 65 deletions gratipay/models/participant/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import gratipay
from gratipay.exceptions import EmailAlreadyVerified, EmailTaken, CannotRemovePrimaryEmail
from gratipay.exceptions import EmailNotVerified, TooManyEmailAddresses, EmailNotOnFile, NoPackages
from gratipay.exceptions import EmailNotVerified, TooManyEmailAddresses, EmailNotOnFile
from gratipay.security.crypto import constant_time_compare
from gratipay.utils import encode_for_querystring

Expand Down Expand Up @@ -190,31 +190,6 @@ def get_email_verification_nonce(self, c, email):
return nonce


def start_package_claims(self, c, nonce, *packages):
"""Takes a cursor, nonce and list of packages, inserts into ``claims``
and returns ``None`` (or raise :py:exc:`NoPackages`).
"""
if not packages:
raise NoPackages()

# We want to make a single db call to insert all claims, so we need to
# do a little SQL construction. Do it in such a way that we still avoid
# Python string interpolation (~= SQLi vector).

extra_sql, values = [], []
for p in packages:
extra_sql.append('(%s, %s)')
values += [nonce, p.id]
c.run('INSERT INTO claims (nonce, package_id) VALUES' + ', '.join(extra_sql), values)
self.app.add_event( c
, 'participant'
, dict( id=self.id
, action='start-claim'
, values=dict(package_ids=[p.id for p in packages])
)
)


def set_primary_email(self, email, cursor=None):
"""Set the primary email address for the participant.
"""
Expand Down Expand Up @@ -279,20 +254,6 @@ def finish_email_verification(self, email, nonce):
return VERIFICATION_SUCCEEDED, packages, paypal_updated


def get_packages_claiming(self, cursor, nonce):
"""Given a nonce, return :py:class:`~gratipay.models.package.Package`
objects associated with it.
"""
return cursor.all("""
SELECT p.*::packages
FROM packages p
JOIN claims c
ON p.id = c.package_id
WHERE c.nonce=%s
ORDER BY p.name ASC
""", (nonce,))


def save_email_address(self, cursor, address):
"""Given an email address, modify the database.
Expand All @@ -317,31 +278,6 @@ def save_email_address(self, cursor, address):
self.set_primary_email(address, cursor)


def finish_package_claims(self, cursor, nonce, *packages):
"""Create teams if needed and associate them with the packages.
"""
if not packages:
raise NoPackages()

package_ids, teams, team_ids = [], [], []
for package in packages:
package_ids.append(package.id)
team = package.get_or_create_linked_team(cursor, self)
teams.append(team)
team_ids.append(team.id)
review_url = self.app.project_review_repo.create_issue(*teams)

cursor.run('DELETE FROM claims WHERE nonce=%s', (nonce,))
cursor.run('UPDATE teams SET review_url=%s WHERE id=ANY(%s)', (review_url, team_ids,))
self.app.add_event( cursor
, 'participant'
, dict( id=self.id
, action='finish-claim'
, values=dict(package_ids=package_ids)
)
)


def get_email(self, address, cursor=None, and_lock=False):
"""Return a record for a single email address on file for this participant.
Expand Down
70 changes: 70 additions & 0 deletions gratipay/models/participant/packages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

from gratipay.exceptions import NoPackages


class Packages(object):

def start_package_claims(self, c, nonce, *packages):
"""Takes a cursor, nonce and list of packages, inserts into ``claims``
and returns ``None`` (or raise :py:exc:`NoPackages`).
"""
if not packages:
raise NoPackages()

# We want to make a single db call to insert all claims, so we need to
# do a little SQL construction. Do it in such a way that we still avoid
# Python string interpolation (~= SQLi vector).

extra_sql, values = [], []
for p in packages:
extra_sql.append('(%s, %s)')
values += [nonce, p.id]
c.run('INSERT INTO claims (nonce, package_id) VALUES' + ', '.join(extra_sql), values)
self.app.add_event( c
, 'participant'
, dict( id=self.id
, action='start-claim'
, values=dict(package_ids=[p.id for p in packages])
)
)


def get_packages_claiming(self, cursor, nonce):
"""Given a nonce, return :py:class:`~gratipay.models.package.Package`
objects associated with it.
"""
return cursor.all("""
SELECT p.*::packages
FROM packages p
JOIN claims c
ON p.id = c.package_id
WHERE c.nonce=%s
ORDER BY p.name ASC
""", (nonce,))


def finish_package_claims(self, cursor, nonce, *packages):
"""Create teams if needed and associate them with the packages.
"""
if not packages:
raise NoPackages()

package_ids, teams, team_ids = [], [], []
for package in packages:
package_ids.append(package.id)
team = package.get_or_create_linked_team(cursor, self)
teams.append(team)
team_ids.append(team.id)
review_url = self.app.project_review_repo.create_issue(*teams)

cursor.run('DELETE FROM claims WHERE nonce=%s', (nonce,))
cursor.run('UPDATE teams SET review_url=%s WHERE id=ANY(%s)', (review_url, team_ids,))
self.app.add_event( cursor
, 'participant'
, dict( id=self.id
, action='finish-claim'
, values=dict(package_ids=package_ids)
)
)
14 changes: 14 additions & 0 deletions gratipay/testing/harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,13 @@ def make_participant(self, username, **kw):
RETURNING participants.*::participants
""", (username, username.lower()))

if 'id' in kw:
new_id = kw.pop('id')
self.db.run( 'UPDATE participants SET id=%s WHERE id=%s'
, (new_id, participant.id)
)
participant.set_attributes(id=new_id)

if 'elsewhere' in kw or 'claimed_time' in kw:
platform = kw.pop('elsewhere', 'github')
self.db.run("""
Expand All @@ -256,6 +263,13 @@ def make_participant(self, username, **kw):
route = ExchangeRoute.insert(participant, 'paypal', '[email protected]')
route.update_error(kw.pop('last_paypal_result'))

# Handle email address
if 'email_address' in kw:
address = kw.pop('email_address')
if address:
self.add_and_verify_email(participant, address)
self.app.email_queue.purge()

# Update participant
verified_in = kw.pop('verified_in', [])
if kw:
Expand Down
Loading

0 comments on commit 8e743ea

Please sign in to comment.