diff --git a/README.md b/README.md
index 9d6f6e6a71..525052cfb3 100644
--- a/README.md
+++ b/README.md
@@ -423,7 +423,7 @@ some fake data, so that more of the site is functional, run this command:
API
===
-The Gratipay API is comprised of these six endpoints:
+The Gratipay API is comprised of these four endpoints:
**[/about/charts.json](https://gratipay.com/about/charts.json)**
([source](https://github.com/gratipay/gratipay.com/tree/master/www/about/charts.json.spt))—public—Returns
@@ -440,28 +440,12 @@ charts page used to use this.
an object giving a point-in-time snapshot of Gratipay. The
[stats](https://gratipay.com/about/stats.html) page displays the same info.
-**/`%username`/charts.json**
-([example](https://gratipay.com/Gratipay/charts.json),
-[source](https://github.com/gratipay/gratipay.com/tree/master/www/%25username/charts.json.spt))—public—Returns
-an array of objects, one per week, showing aggregate numbers over time for the
-given user.
-
**/`%username`/public.json**
([example](https://gratipay.com/Gratipay/public.json),
[source](https://github.com/gratipay/gratipay.com/tree/master/www/%25username/public.json.spt))—public—Returns an object with these keys:
- - "receiving"—an estimate of the amount the given participant will
- receive this week
-
- - "my_tip"—logged-in user's tip to the Gratipay participant in
- question; possible values are:
-
- - `undefined` (key not present)—there is no logged-in user
- - "self"—logged-in user is the participant in question
- - `null`—user has never tipped this participant
- - "0.00"—user used to tip this participant
- - "3.00"—user tips this participant the given amount
-
+ - "taking"—an estimate of the amount the given participant will
+ take from Teams this week
- "elsewhere"—participant's connected accounts elsewhere; returns an object with these keys:
@@ -479,34 +463,6 @@ given user.
- `http://www.openstreetmap.org/user/%openstreetmap_username`
-**/`%username`/tips.json**
-([source](https://github.com/gratipay/gratipay.com/tree/master/www/%25username/tips.json.spt))—private—Responds
-to `GET` with an array of objects representing your current tips. `POST` the
-same structure back in order to update tips in bulk (be sure to set
-`Content-Type` to `application/json` instead of
-`application/x-www-form-urlencoded`). You can `POST` a partial array to update
-a subset of your tips. The response to a `POST` will be only the subset you
-updated. If the `amount` is `"error"` then there will also be an `error`
-attribute with a one-word error code. If you include an `also_prune` key in the
-querystring (not the body!) with a value of `yes`, `true`, or `1`, then any
-tips not in the array you `POST` will be zeroed out.
-
-NOTE: The amounts must be encoded as a string (rather than a number).
-Additionally, currently, the only supported platform is 'gratipay' ('gittip'
-still works for backwards-compatibility).
-
-This endpoint requires authentication. Look for your user ID and API key on your
-[account page](https://gratipay.com/about/me/settings/), and pass them using basic
-auth. E.g.:
-
-```
-curl https://gratipay.com/foobar/tips.json \
- -u $userid:$api_key \
- -X POST \
- -d'[{"username":"bazbuz", "platform":"gratipay", "amount": "1.00"}]' \
- -H"Content-Type: application/json"
-```
-
API Implementations
-------------------
diff --git a/gratipay/billing/exchanges.py b/gratipay/billing/exchanges.py
index 424e37d771..a39806ba0d 100644
--- a/gratipay/billing/exchanges.py
+++ b/gratipay/billing/exchanges.py
@@ -311,6 +311,7 @@ def record_exchange_result(db, exchange_id, status, error, participant):
""", locals())
assert participant.username == username
assert isinstance(route, ExchangeRoute)
+ route.set_attributes(participant=participant) # XXX Red hot hack!
if amount < 0:
amount -= fee
@@ -325,7 +326,7 @@ def propagate_exchange(cursor, participant, route, error, amount):
"""Propagates an exchange's result to the participant's balance and the
route's status.
"""
- route.update_error(error or '', propagate=False)
+ route.update_error(error or '')
new_balance = cursor.one("""
UPDATE participants
SET balance=(balance + %s)
diff --git a/gratipay/billing/payday.py b/gratipay/billing/payday.py
index ab29ad2801..717cf8f49d 100644
--- a/gratipay/billing/payday.py
+++ b/gratipay/billing/payday.py
@@ -29,9 +29,6 @@
with open('sql/payday.sql') as f:
PAYDAY = f.read()
-with open('sql/fake_payday.sql') as f:
- FAKE_PAYDAY = f.read()
-
class ExceptionWrapped(Exception): pass
@@ -80,7 +77,6 @@ class Payday(object):
update_balances
take_over_balances
update_stats
- update_cached_amounts
end
"""
@@ -137,7 +133,6 @@ def run(self):
self.mark_stage_done()
if self.stage < 2:
self.update_stats()
- self.update_cached_amounts()
self.mark_stage_done()
self.end()
@@ -462,12 +457,6 @@ def update_stats(self):
log("Updated payday stats.")
- def update_cached_amounts(self):
- with self.db.get_cursor() as cursor:
- cursor.execute(FAKE_PAYDAY)
- log("Updated receiving amounts.")
-
-
def end(self):
self.ts_end = self.db.one("""\
diff --git a/gratipay/models/exchange_route.py b/gratipay/models/exchange_route.py
index 2ea41b87c5..473415f720 100644
--- a/gratipay/models/exchange_route.py
+++ b/gratipay/models/exchange_route.py
@@ -15,11 +15,15 @@ def __bool__(self):
@classmethod
def from_id(cls, id):
- return cls.db.one("""
+ r = cls.db.one("""
SELECT r.*::exchange_routes
FROM exchange_routes r
WHERE id = %(id)s
""", locals())
+ if r:
+ from gratipay.models.participant import Participant # XXX Red hot hack!
+ r.set_attributes(participant=Participant.from_id(r.participant))
+ return r
@classmethod
def from_network(cls, participant, network):
@@ -31,7 +35,7 @@ def from_network(cls, participant, network):
AND network = %(network)s
""", locals())
if r:
- r.__dict__['participant'] = participant
+ r.set_attributes(participant=participant)
return r
@classmethod
@@ -45,7 +49,7 @@ def from_address(cls, participant, network, address):
AND address = %(address)s
""", locals())
if r:
- r.__dict__['participant'] = participant
+ r.set_attributes(participant=participant)
return r
@classmethod
@@ -57,9 +61,9 @@ def insert(cls, participant, network, address, error='', fee_cap=None):
VALUES (%(participant_id)s, %(network)s, %(address)s, %(error)s, %(fee_cap)s)
RETURNING exchange_routes.*::exchange_routes
""", locals())
- if network == 'balanced-cc':
+ if network == 'braintree-cc':
participant.update_giving_and_teams()
- r.__dict__['participant'] = participant
+ r.set_attributes(participant=participant)
return r
def invalidate(self):
@@ -69,11 +73,12 @@ def invalidate(self):
# For Paypal, we remove the record entirely to prevent
# an integrity error if the user tries to add the route again
if self.network == 'paypal':
+ # XXX This doesn't sound right. Doesn't this corrupt history pages?
self.db.run("DELETE FROM exchange_routes WHERE id=%s", (self.id,))
else:
self.update_error('invalidated')
- def update_error(self, new_error, propagate=True):
+ def update_error(self, new_error):
id = self.id
old_error = self.error
if old_error == 'invalidated':
@@ -85,9 +90,19 @@ def update_error(self, new_error, propagate=True):
""", locals())
self.set_attributes(error=new_error)
- # Update the receiving amounts of tippees if requested and necessary
- if not propagate or self.network != 'balanced-cc':
+ # Update cached amounts if requested and necessary
+ if self.network != 'braintree-cc':
return
if self.participant.is_suspicious or bool(new_error) == bool(old_error):
return
- self.participant.update_giving_and_teams()
+
+
+ # XXX *White* hot hack!
+ # =====================
+ # During payday, participant is a record from a select of
+ # payday_participants (or whatever), *not* an actual Participant
+ # object. We need the real deal so we can use a method on it ...
+
+ from gratipay.models.participant import Participant
+ participant = Participant.from_username(self.participant.username)
+ participant.update_giving_and_teams()
diff --git a/gratipay/models/participant.py b/gratipay/models/participant.py
index 4a32d8d8d5..3cc7348895 100644
--- a/gratipay/models/participant.py
+++ b/gratipay/models/participant.py
@@ -1,12 +1,4 @@
-"""*Participant* is the name Gratipay gives to people and groups that are known
-to Gratipay. We've got a ``participants`` table in the database, and a
-:py:class:`Participant` class that we define here. We distinguish several kinds
-of participant, based on certain properties.
-
- - *Stub* participants
- - *Organizations* are plural participants
- - *Teams* are plural participants with members
-
+"""Participants on Gratipay give payments and take payroll.
"""
from __future__ import print_function, unicode_literals
@@ -42,7 +34,6 @@
)
from gratipay.models import add_event
-from gratipay.models._mixin_team import MixinTeam
from gratipay.models.account_elsewhere import AccountElsewhere
from gratipay.models.exchange_route import ExchangeRoute
from gratipay.models.team import Team
@@ -61,7 +52,7 @@
USERNAME_MAX_SIZE = 32
-class Participant(Model, MixinTeam):
+class Participant(Model):
"""Represent a Gratipay participant.
"""
@@ -257,7 +248,7 @@ def upsert_statement(self, lang, statement):
@property
def usage(self):
- return max(self.giving, self.receiving)
+ return max(self.giving, self.taking)
@property
def suggested_payment(self):
@@ -382,7 +373,6 @@ def clear_personal_information(self, cursor):
UPDATE participants
SET anonymous_giving=False
- , anonymous_receiving=False
, number='singular'
, avatar_url=NULL
, email_address=NULL
@@ -390,8 +380,7 @@ def clear_personal_information(self, cursor):
, session_token=NULL
, session_expires=now()
, giving=0
- , receiving=0
- , npatrons=0
+ , taking=0
WHERE username=%(username)s
RETURNING *;
@@ -959,51 +948,36 @@ def update_giving(self, cursor=None):
RETURNING *
""", (self.username,))
- giving = (cursor or self.db).one("""
+ r = (cursor or self.db).one("""
+ WITH pi AS (
+ SELECT amount
+ FROM current_payment_instructions cpi
+ JOIN teams t ON t.slug = cpi.team
+ WHERE participant = %(username)s
+ AND amount > 0
+ AND is_funded
+ AND t.is_approved
+ )
UPDATE participants p
- SET giving = COALESCE((
- SELECT sum(amount)
- FROM current_payment_instructions cpi
- JOIN teams t ON t.slug = cpi.team
- WHERE participant = %(username)s
- AND amount > 0
- AND is_funded
- AND t.is_approved
- ), 0)
+ SET giving = COALESCE((SELECT sum(amount) FROM pi), 0)
+ , ngiving_to = COALESCE((SELECT count(amount) FROM pi), 0)
WHERE p.username=%(username)s
- RETURNING giving
+ RETURNING giving, ngiving_to
""", dict(username=self.username))
- self.set_attributes(giving=giving)
+ self.set_attributes(giving=r.giving, ngiving_to=r.ngiving_to)
return updated
- def update_receiving(self, cursor=None):
- if self.IS_PLURAL:
- old_takes = self.compute_actual_takes(cursor=cursor)
- r = (cursor or self.db).one("""
- WITH our_tips AS (
- SELECT amount
- FROM current_tips
- JOIN participants p2 ON p2.username = tipper
- WHERE tippee = %(username)s
- AND p2.is_suspicious IS NOT true
- AND amount > 0
- AND is_funded
- )
- UPDATE participants p
- SET receiving = (COALESCE((
- SELECT sum(amount)
- FROM our_tips
- ), 0) + taking)
- , npatrons = COALESCE((SELECT count(*) FROM our_tips), 0)
- WHERE p.username = %(username)s
- RETURNING receiving, npatrons
+ def update_taking(self, cursor=None):
+ (cursor or self.db).run("""
+
+ UPDATE participants
+ SET taking=COALESCE((SELECT sum(receiving) FROM teams WHERE owner=%(username)s), 0)
+ , ntaking_from=COALESCE((SELECT count(*) FROM teams WHERE owner=%(username)s), 0)
+ WHERE username=%(username)s
+
""", dict(username=self.username))
- self.set_attributes(receiving=r.receiving, npatrons=r.npatrons)
- if self.IS_PLURAL:
- new_takes = self.compute_actual_takes(cursor=cursor)
- self.update_taking(old_takes, new_takes, cursor=cursor)
def update_is_free_rider(self, is_free_rider, cursor=None):
@@ -1120,12 +1094,12 @@ def change_username(self, suggested):
def get_og_title(self):
out = self.username
- receiving = self.receiving
giving = self.giving
- if (giving > receiving) and not self.anonymous_giving:
+ taking = self.taking
+ if (giving > taking) and not self.anonymous_giving:
out += " gives $%.2f/wk" % giving
- elif receiving > 0 and not self.anonymous_receiving:
- out += " receives $%.2f/wk" % receiving
+ elif taking > 0:
+ out += " takes $%.2f/wk" % taking
else:
out += " is"
return out + " on Gratipay"
@@ -1170,7 +1144,6 @@ def reserve(cursor, username):
, session_token=NULL
, session_expires=now()
, giving = 0
- , receiving = 0
, taking = 0
WHERE username=%s
RETURNING username
@@ -1522,8 +1495,8 @@ def take_over(self, account, have_confirmation=False):
self.update_avatar()
- # Note: the order matters here, receiving needs to be updated before giving
- self.update_receiving()
+ # Note: the order ... doesn't actually matter here.
+ self.update_taking()
self.update_giving()
def to_dict(self, details=False, inquirer=None):
@@ -1532,47 +1505,27 @@ def to_dict(self, details=False, inquirer=None):
, 'avatar': self.avatar_url
, 'number': self.number
, 'on': 'gratipay'
- }
+ }
if not details:
return output
- # Key: npatrons
- output['npatrons'] = self.npatrons
-
- # Key: receiving
+ # Key: taking
# Values:
- # null - user is receiving anonymously
- # 3.00 - user receives this amount in tips
- if not self.anonymous_receiving:
- receiving = str(self.receiving)
- else:
- receiving = None
- output['receiving'] = receiving
+ # 3.00 - user takes this amount in payroll
+ output['taking'] = str(self.taking)
+ output['ntaking_from'] = self.ntaking_from
# Key: giving
# Values:
# null - user is giving anonymously
- # 3.00 - user gives this amount in tips
- if not self.anonymous_giving:
- giving = str(self.giving)
- else:
+ # 3.00 - user gives this amount
+ if self.anonymous_giving:
giving = None
+ else:
+ giving = str(self.giving)
output['giving'] = giving
-
- # Key: my_tip
- # Values:
- # undefined - user is not authenticated
- # "self" - user == participant
- # null - user has never tipped this person
- # 0.00 - user used to tip this person but now doesn't
- # 3.00 - user tips this person this amount
- if inquirer:
- if inquirer.username == self.username:
- my_tip = 'self'
- else:
- my_tip = inquirer.get_tip_to(self.username)['amount']
- output['my_tip'] = str(my_tip)
+ output['ngiving_to'] = self.ngiving_to
# Key: elsewhere
accounts = self.get_accounts_elsewhere()
diff --git a/gratipay/models/team.py b/gratipay/models/team.py
index 9c24eb599d..fd2fa32350 100644
--- a/gratipay/models/team.py
+++ b/gratipay/models/team.py
@@ -1,4 +1,4 @@
-"""Teams on Gratipay are plural participants with members.
+"""Teams on Gratipay receive payments and distribute payroll.
"""
from postgres.orm import Model
@@ -85,16 +85,24 @@ def update_receiving(self, cursor=None):
AND is_funded
)
UPDATE teams t
- SET receiving = (COALESCE((
- SELECT sum(amount)
- FROM our_receiving
- ), 0))
- , nsupporters = COALESCE((SELECT count(*) FROM our_receiving), 0)
+ SET receiving = COALESCE((SELECT sum(amount) FROM our_receiving), 0)
+ , nreceiving_from = COALESCE((SELECT count(*) FROM our_receiving), 0)
+ , distributing = COALESCE((SELECT sum(amount) FROM our_receiving), 0)
+ , ndistributing_to = 1
WHERE t.slug = %(slug)s
- RETURNING receiving, nsupporters
+ RETURNING receiving, nreceiving_from, distributing, ndistributing_to
""", dict(slug=self.slug))
- self.set_attributes(receiving=r.receiving, nsupporters=r.nsupporters)
+
+ # 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()
+
+ self.set_attributes( receiving=r.receiving
+ , nreceiving_from=r.nreceiving_from
+ , distributing=r.distributing
+ , ndistributing_to=r.ndistributing_to
+ )
@property
def status(self):
diff --git a/gratipay/testing/__init__.py b/gratipay/testing/__init__.py
index 4ddb31b449..d127dbbe32 100644
--- a/gratipay/testing/__init__.py
+++ b/gratipay/testing/__init__.py
@@ -235,7 +235,7 @@ def make_exchange(self, route, amount, fee, participant, status='succeeded', err
return e_id
- def make_tip(self, tipper, tippee, amount, update_self=True, update_tippee=True, cursor=None):
+ def make_tip(self, tipper, tippee, amount, cursor=None):
"""Given a Participant or username, and amount as str, returns a dict.
We INSERT instead of UPDATE, so that we have history to explore. The
@@ -286,12 +286,6 @@ def make_tip(self, tipper, tippee, amount, update_self=True, update_tippee=True,
args = dict(tipper=tipper.username, tippee=tippee.username, amount=amount)
t = (cursor or self.db).one(NEW_TIP, args)
- if update_self:
- # Update giving amount of tipper
- tipper.update_giving(cursor)
- if update_tippee:
- # Update receiving amount of tippee
- tippee.update_receiving(cursor)
if tippee.username == 'Gratipay':
# Update whether the tipper is using Gratipay for free
tipper.update_is_free_rider(None if amount == 0 else False, cursor)
diff --git a/gratipay/utils/__init__.py b/gratipay/utils/__init__.py
index f0710318b4..867f4aa7f5 100644
--- a/gratipay/utils/__init__.py
+++ b/gratipay/utils/__init__.py
@@ -140,12 +140,12 @@ def update_global_stats(website):
website.gnactive = stats[0]
website.gtransfer_volume = stats[1]
- nbackers = website.db.one("""
- SELECT npatrons
- FROM participants
- WHERE username = 'Gratipay'
+ nreceiving_from = website.db.one("""
+ SELECT nreceiving_from
+ FROM teams
+ WHERE slug = 'Gratipay'
""", default=0)
- website.support_current = cur = int(round(nbackers / stats[0] * 100)) if stats[0] else 0
+ website.support_current = cur = int(round(nreceiving_from / stats[0] * 100)) if stats[0] else 0
if cur < 10: goal = 20
elif cur < 15: goal = 30
elif cur < 25: goal = 40
diff --git a/gratipay/utils/fake_data.py b/gratipay/utils/fake_data.py
index 5b7e65f340..fcb573aa91 100644
--- a/gratipay/utils/fake_data.py
+++ b/gratipay/utils/fake_data.py
@@ -61,7 +61,6 @@ def fake_participant(db, number="singular", is_admin=False):
, is_admin=is_admin
, balance=0
, anonymous_giving=(random.randrange(5) == 0)
- , anonymous_receiving=(number != 'plural' and random.randrange(5) == 0)
, balanced_customer_href=faker.uri()
, is_suspicious=False
, claimed_time=faker.date_time_this_year()
@@ -99,7 +98,7 @@ def fake_team(db, teamowner):
, owner=teamowner.username
, is_approved=random.sample(isapproved,1)[0]
, receiving=0.1
- , nmembers=3
+ , nreceiving_from=3
)
except IntegrityError:
return fake_team(db, teamowner)
diff --git a/js/gratipay/payments.js b/js/gratipay/payments.js
index b4ed5e2fbb..15871e7495 100644
--- a/js/gratipay/payments.js
+++ b/js/gratipay/payments.js
@@ -8,7 +8,7 @@ Gratipay.payments.init = function() {
root: $('.your-payment.js-edit'),
success: function(data) {
Gratipay.notification(data.msg, 'success');
- Gratipay.payments.afterTipChange(data);
+ Gratipay.payments.afterPaymentChange(data);
}
});
@@ -58,17 +58,17 @@ Gratipay.payments.initSupportGratipay = function() {
};
-Gratipay.payments.afterTipChange = function(data) {
+Gratipay.payments.afterPaymentChange = function(data) {
$('.my-total-giving').text(data.total_giving_l);
- $('.total-receiving[data-team="'+data.team_id+'"]').text(data.total_receiving_team_l);
+ $('.total-receiving[data-team="'+data.team_id+'"]').text(data.total_receiving_l);
$('#payment-prompt').toggleClass('needed', data.amount > 0);
- $('.nsupporters[data-team="'+data.team_id+'"]').text(data.nsupporters);
+ $('.nreceiving-from[data-team="'+data.team_id+'"]').text(data.nreceiving_from);
var $your_payment = $('.your-payment[data-team="'+data.team_id+'"]');
- if ($your_payment) {
+ if ($your_payment.length === 1) {
var $input = $your_payment.find('input');
$input[0].defaultValue = $input.val();
- $your_payment.find('span.amount').text(data.amount_l);
+ $your_payment.find('.view span.amount').text(data.amount_l);
$your_payment.find('.edit').toggleClass('not-zero', data.amount > 0);
$your_payment.find('.stop').toggleClass('zero', data.amount === 0);
}
@@ -80,7 +80,7 @@ Gratipay.payments.set = function(team, amount, callback) {
// send request to set up a recurring payment
$.post('/' + team + '/payment-instruction.json', { amount: amount }, function(data) {
if (callback) callback(data);
- Gratipay.payments.afterTipChange(data);
+ Gratipay.payments.afterPaymentChange(data);
})
.fail(Gratipay.error);
};
diff --git a/js/gratipay/settings.js b/js/gratipay/settings.js
index c9a3a1af95..c45efe7999 100644
--- a/js/gratipay/settings.js
+++ b/js/gratipay/settings.js
@@ -15,39 +15,6 @@ Gratipay.settings.init = function() {
});
- // Wire up account type knob.
- // ==========================
-
- $('.number input').click(function(e) {
- var $input = $(this);
-
- e.preventDefault();
-
- function post(confirmed) {
- jQuery.ajax({
- url: '../number.json',
- type: 'POST',
- data: {
- number: $input.val(),
- confirmed: confirmed
- },
- success: function(data) {
- if (data.confirm) {
- if (confirm(data.confirm)) return post(true);
- return;
- }
- if (data.number) {
- $input.prop('checked', true);
- Gratipay.notification(data.msg || "Success", 'success');
- $('li.members').toggleClass('hidden', data.number !== 'plural');
- }
- },
- error: Gratipay.error,
- });
- }
- post();
- });
-
// Wire up privacy settings.
// =========================
diff --git a/scss/components/banner.scss b/scss/components/banner.scss
index f356910d35..f6df51be08 100644
--- a/scss/components/banner.scss
+++ b/scss/components/banner.scss
@@ -1,13 +1,12 @@
#banner {
-
- a {
- display: block;
- }
+ position: relative;
+ z-index: 0;
.avatar {
position: relative;
height: 192px;
- width: 256px; // to allow for blur
+ width: 100%;
+ z-index: 2;
overflow: hidden;
img {
@@ -31,18 +30,25 @@
left: 136px;
bottom: 24px;
box-shadow: none;
+ z-index: 4;
}
}
.is-suspicious-label {
text-align: right;
- font-size: 10px;
+ font-size: 12px;
text-transform: uppercase;
display: block;
color: $white;
position: absolute;
- bottom: 0px;
- right: 80px;
+ bottom: 24px;
+ left: 24px;
+ z-index: 3;
+ background: $black;
+ border: 2px solid $white;
+ @include border-radius(5px);
+
+ padding: 1px 8px;
}
&.is-suspicious {
@@ -54,13 +60,54 @@
position: absolute;
top: 16px;
right: 16px;
+ z-index: 2;
color: $white;
text-shadow: -1px 1px 1px rgba($brown, 1);
text-align: right;
+ background: $black;
+ padding: 3px 7px;
+ @include border-radius(5px);
+ box-shadow: 0px 0px 32px 16px rgba($black, 0.5);
+ border: 2px solid $white;
+
+ p {
+ margin: 0;
+ padding: 0;
+ }
+
+ table {
+ margin: 0 0 6px auto;
+ th {
+ text-align: right;
+ }
+ td {
+ padding: 0 0 0 12px;
+ &:first-child {
+ padding-left: 0;
+ }
+ }
+ .label {
+ text-align: center;
+ }
+ }
+ }
+
+ .admin-details {
+ position: absolute;
+ top: 24px;
+ left: 24px;
+ max-width: 45%;
+ padding: 2px 16px 1px;
+ color: white;
+ background: $black;
+ border: 2px solid $white;
+ z-index: 2;
+ @include border-radius(5px);
+ box-shadow: 0px 0px 32px 16px rgba($black, 0.5);
a {
- color: $white;
text-decoration: underline;
+ color: $white;
}
}
@@ -69,6 +116,7 @@
position: absolute;
bottom: 4px;
right: 16px;
+ z-index: 2;
text-align: right;
color: $white;
text-shadow: -1px 1px 1px rgba($brown, 1);
diff --git a/scss/components/nav.scss b/scss/components/nav.scss
index e61794becf..f98e6c2567 100644
--- a/scss/components/nav.scss
+++ b/scss/components/nav.scss
@@ -121,6 +121,7 @@
a {
display: inline-block;
padding: 16px 16px 12px;
+ font-weight: normal;
.icon {
font: normal 20px/20px 'icomoon';
diff --git a/scss/layouts/responsiveness.scss b/scss/layouts/responsiveness.scss
index c1488efb1a..fc0aa55a7d 100644
--- a/scss/layouts/responsiveness.scss
+++ b/scss/layouts/responsiveness.scss
@@ -1,4 +1,7 @@
@media (max-width: 600px) {
+ .luxury {
+ display: none;
+ }
#header {
text-align: center;
h1 {
@@ -11,7 +14,6 @@
height: 128px;
.avatar {
height: 128px;
- width: 144px;
img {
width: 112px;
height: 112px;
@@ -32,9 +34,24 @@
}
}
.is-suspicious-label {
- display: none;
+ font-size: 10px;
+ bottom: 13px;
+ left: 13px;
}
}
+ .details {
+ font-size: 10px;
+ box-shadow: 0px 0px 16px 8px rgba($black, 0.5);
+ @include border-radius(3px);
+ top: 8px;
+ right: 8px;
+ }
+ .admin-details {
+ top: 13px;
+ left: 13px;
+ font-size: 10px;
+ padding: 1px 8px;
+ }
h1 {
font-size: 32px;
line-height: 40px;
diff --git a/sql/branch.sql b/sql/branch.sql
new file mode 100644
index 0000000000..bb4733d8b2
--- /dev/null
+++ b/sql/branch.sql
@@ -0,0 +1,14 @@
+BEGIN;
+
+ ALTER TABLE participants DROP COLUMN anonymous_receiving;
+ ALTER TABLE participants DROP COLUMN npatrons;
+ ALTER TABLE participants DROP COLUMN receiving;
+
+ ALTER TABLE participants ADD COLUMN ngiving_to INTEGER NOT NULL DEFAULT 0;
+ ALTER TABLE participants ADD COLUMN ntaking_from INTEGER NOT NULL DEFAULT 0;
+
+ ALTER TABLE teams RENAME COLUMN nsupporters TO nreceiving_from;
+ ALTER TABLE teams RENAME COLUMN nmembers TO ndistributing_to;
+ ALTER TABLE teams RENAME COLUMN payroll TO distributing;
+
+END;
diff --git a/sql/fake_payday.sql b/sql/fake_payday.sql
deleted file mode 100644
index fc2720c1e7..0000000000
--- a/sql/fake_payday.sql
+++ /dev/null
@@ -1,186 +0,0 @@
--- Create the necessary temporary tables and indexes
-
-CREATE TEMPORARY TABLE temp_participants ON COMMIT DROP AS
- SELECT username
- , claimed_time
- , balance AS fake_balance
- , 0::numeric(35,2) AS giving
- , 0::numeric(35,2) AS taking
- , 0::numeric(35,2) AS receiving
- , 0 as npatrons
- , ( SELECT count(*)
- FROM exchange_routes r
- WHERE r.participant = p.id
- AND network = 'balanced-cc'
- AND error = ''
- ) > 0 AS credit_card_ok
- FROM participants p
- WHERE is_suspicious IS NOT true;
-
-CREATE UNIQUE INDEX ON temp_participants (username);
-
-CREATE TEMPORARY TABLE temp_tips ON COMMIT DROP AS
- SELECT t.id, tipper, tippee, amount, (p2.claimed_time IS NOT NULL) AS claimed
- FROM current_tips t
- JOIN temp_participants p ON p.username = t.tipper
- JOIN temp_participants p2 ON p2.username = t.tippee
- WHERE t.amount > 0
- ORDER BY p2.claimed_time IS NULL, p.claimed_time ASC, t.ctime ASC;
-
-CREATE INDEX ON temp_tips (tipper);
-CREATE INDEX ON temp_tips (tippee);
-ALTER TABLE temp_tips ADD COLUMN is_funded boolean NOT NULL DEFAULT false;
-
-CREATE TEMPORARY TABLE temp_takes
-( team text
-, member text
-, amount numeric(35,2)
-) ON COMMIT DROP;
-
-
--- Create a trigger to process tips
-
-CREATE OR REPLACE FUNCTION fake_tip() RETURNS trigger AS $$
- DECLARE
- tipper temp_participants;
- BEGIN
- tipper := (
- SELECT p.*::temp_participants
- FROM temp_participants p
- WHERE username = NEW.tipper
- );
- IF (NEW.amount > tipper.fake_balance AND NOT tipper.credit_card_ok) THEN
- RETURN NULL;
- END IF;
- IF (NEW.claimed) THEN
- UPDATE temp_participants
- SET fake_balance = (fake_balance - NEW.amount)
- , giving = (giving + NEW.amount)
- WHERE username = NEW.tipper;
- ELSE
- UPDATE temp_participants
- SET fake_balance = (fake_balance - NEW.amount)
- WHERE username = NEW.tipper;
- END IF;
- UPDATE temp_participants
- SET fake_balance = (fake_balance + NEW.amount)
- , receiving = (receiving + NEW.amount)
- , npatrons = (npatrons + 1)
- WHERE username = NEW.tippee;
- RETURN NEW;
- END;
-$$ LANGUAGE plpgsql;
-
-CREATE TRIGGER fake_tip BEFORE UPDATE OF is_funded ON temp_tips
- FOR EACH ROW
- WHEN (NEW.is_funded IS true AND OLD.is_funded IS NOT true)
- EXECUTE PROCEDURE fake_tip();
-
-
--- Create a trigger to process takes
-
-CREATE OR REPLACE FUNCTION fake_take() RETURNS trigger AS $$
- DECLARE
- actual_amount numeric(35,2);
- team_balance numeric(35,2);
- BEGIN
- team_balance := (
- SELECT fake_balance
- FROM temp_participants
- WHERE username = NEW.team
- );
- IF (team_balance <= 0) THEN RETURN NULL; END IF;
- actual_amount := NEW.amount;
- IF (team_balance < NEW.amount) THEN
- actual_amount := team_balance;
- END IF;
- UPDATE temp_participants
- SET fake_balance = (fake_balance - actual_amount)
- WHERE username = NEW.team;
- UPDATE temp_participants
- SET fake_balance = (fake_balance + actual_amount)
- , taking = (taking + actual_amount)
- , receiving = (receiving + actual_amount)
- WHERE username = NEW.member;
- RETURN NULL;
- END;
-$$ LANGUAGE plpgsql;
-
-CREATE TRIGGER fake_take AFTER INSERT ON temp_takes
- FOR EACH ROW EXECUTE PROCEDURE fake_take();
-
-
--- Create a function to settle whole tip graph
-
-CREATE OR REPLACE FUNCTION settle_tip_graph() RETURNS void AS $$
- DECLARE
- count integer NOT NULL DEFAULT 0;
- i integer := 0;
- BEGIN
- LOOP
- i := i + 1;
- WITH updated_rows AS (
- UPDATE temp_tips
- SET is_funded = true
- WHERE is_funded IS NOT true
- RETURNING *
- )
- SELECT COUNT(*) FROM updated_rows INTO count;
- IF (count = 0) THEN
- EXIT;
- END IF;
- IF (i > 50) THEN
- RAISE 'Reached the maximum number of iterations';
- END IF;
- END LOOP;
- END;
-$$ LANGUAGE plpgsql;
-
-
--- Start fake payday
-
--- Step 1: tips
-UPDATE temp_tips t
- SET is_funded = true
- FROM temp_participants p
- WHERE p.username = t.tipper
- AND p.credit_card_ok;
-
-SELECT settle_tip_graph();
-
--- Step 2: team takes
-INSERT INTO temp_takes
- SELECT team, member, amount
- FROM current_takes t
- WHERE t.amount > 0
- AND t.team IN (SELECT username FROM temp_participants)
- AND t.member IN (SELECT username FROM temp_participants)
- ORDER BY ctime DESC;
-
--- Step 3: tips again
-SELECT settle_tip_graph();
-
--- Step 4: update the real tables
-UPDATE tips t
- SET is_funded = tt.is_funded
- FROM temp_tips tt
- WHERE t.id = tt.id
- AND t.is_funded <> tt.is_funded;
-
-UPDATE participants p
- SET giving = p2.giving
- , taking = p2.taking
- , receiving = p2.receiving
- , npatrons = p2.npatrons
- FROM temp_participants p2
- WHERE p.username = p2.username
- AND ( p.giving <> p2.giving OR
- p.taking <> p2.taking OR
- p.receiving <> p2.receiving OR
- p.npatrons <> p2.npatrons
- );
-
--- Clean up functions
-DROP FUNCTION fake_take() CASCADE;
-DROP FUNCTION fake_tip() CASCADE;
-DROP FUNCTION settle_tip_graph() CASCADE;
diff --git a/templates/profile-details.html b/templates/profile-details.html
deleted file mode 100644
index cbd150dab3..0000000000
--- a/templates/profile-details.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
{{ _('Joined') }} {{ to_age(participant.claimed_time, add_direction=True) }}.
- {% elif participant.is_closed %} -Closed {{ to_age(participant.closed_time, add_direction=True) }}.
- {% else %} - {% set absorption = website.db.one( "select * from absorptions where archived_as=%s" - , (participant.username,) - ) %} -Formerly {{ absorption.absorbed_was }}.
-Absorbed by {{ absorption.absorbed_by }} - {{ to_age(absorption.timestamp, add_direction=True) }}.
- {% endif %} -
{{ _('Joined') }} {{ to_age(participant.claimed_time, add_direction=True) }}.
- {% elif participant.is_closed %} -Closed {{ to_age(participant.closed_time, add_direction=True) }}.
- {% else %} +{{ _('Joined') }} {{ to_age(participant.claimed_time, add_direction=True) }}
+ {% endif %} + +Formerly {{ absorption.absorbed_was }}.
-Absorbed by {{ absorption.absorbed_by }} - {{ to_age(absorption.timestamp, add_direction=True) }}.
+
{{ _("Weekly") }} | +Σ | +n | +
---|---|---|
{{ _("Receives") }} | +{{ format_currency(team.receiving, 'USD') }} | +{{ team.nreceiving_from }} | +
{{ _("Shares") }} | +{{ format_currency(0, 'USD') }} | +0 | +
+ {% if team.is_approved %} + {{ _('Joined {ago}', ago=to_age(team.ctime, add_direction=True)) }} + {% else %} + {{ _('Applied {ago}', ago=to_age(team.ctime, add_direction=True)) }} + {% endif %} +
+ + +{% if team.status == 'unreviewed' %} - {{ _("Unreviewed") }} | + {{ _("Under Review") }} | {% elif team.status == 'rejected' %} {{ _("Rejected") }} | {% endif %} @@ -53,7 +90,7 @@ banner = name = team.name {% endif %} | {{ _( "owned by {a}~{owner}{_a}" - , a=(''.format(team.owner))|safe + , a=(''.format(team.owner))|safe , owner=team.owner , _a=''|safe ) }} diff --git a/www/%team/payment-instruction.json.spt b/www/%team/payment-instruction.json.spt index 26cec607a5..acf98b7a3b 100644 --- a/www/%team/payment-instruction.json.spt +++ b/www/%team/payment-instruction.json.spt @@ -36,7 +36,7 @@ else: amount = out['amount'] total_giving = user.participant.giving - total_receiving = user.participant.receiving + total_taking = user.participant.taking out["amount"] = str(amount) out["amount_l"] = format_currency(amount, 'USD') @@ -45,17 +45,17 @@ else: out["msg"] += _("Thank you so much for supporting {0}!", team.name) else: out["msg"] = _("You have successfully canceled your payment to {0}.", team.name) - out["nsupporters"] = team.nsupporters + out["nreceiving_from"] = team.nreceiving_from out["team_id"] = team.id out["total_giving"] = str(total_giving) out["total_giving_l"] = format_currency(total_giving, 'USD') + out["total_taking"] = str(total_taking) + out["total_taking_l"] = format_currency(total_taking, 'USD') + + total_receiving = team.receiving out["total_receiving"] = str(total_receiving) out["total_receiving_l"] = format_currency(total_receiving, 'USD') - total_receiving_team = team.receiving - out["total_receiving_team"] = str(total_receiving_team) - out["total_receiving_team_l"] = format_currency(total_receiving_team, 'USD') - if 'ctime' in out: out["ctime"] = str(out['ctime']) out["mtime"] = str(out['mtime']) diff --git a/www/~/%username/receiving/index.html.spt b/www/%team/receiving/index.html.spt similarity index 85% rename from www/~/%username/receiving/index.html.spt rename to www/%team/receiving/index.html.spt index fc3f8b0eb0..5f8d62c9a9 100644 --- a/www/~/%username/receiving/index.html.spt +++ b/www/%team/receiving/index.html.spt @@ -1,13 +1,14 @@ +from aspen import Response from gratipay.utils import get_participant [-----------------------------------------------------------------------------] -participant = get_participant(state, restrict=False) -if participant.anonymous_receiving: - if user.participant != participant and not user.ADMIN: - website.redirect('../', base_url='') - -banner = '~' + participant.username +website.redirect('../', base_url='') +team = get_team(state) +if team.is_approved in (None, False): + if user.ANON: + raise Response(401) +banner = team.name title = _("Receiving") [-----------------------------------------------------------------------------] @@ -29,8 +30,8 @@ title = _("Receiving")
Things we clear immediately include your profile statement, the - tips you're receiving, and those you're giving. You'll also be - removed from any communities and teams you were a part of. If - you're closing a team account, all team members will be removed - from the team.
- -We specifically don't delete your past giving and - receiving history on the site, because that information also - belongs equally to other users (the ones you gave to and - received from).
+Things we clear immediately include your profile statement, + the payroll you're taking, and the payments you're giving.
+ +We specifically don't delete your past giving and + taking history on the site, because that information also belongs + equally to other users (the owners of the Teams you gave to and + took from).
After you close your account, your profile page will say, “The account owner has closed this account.”
diff --git a/www/~/%username/settings/index.html.spt b/www/~/%username/settings/index.html.spt index 18581e3e72..32c6a840a6 100644 --- a/www/~/%username/settings/index.html.spt +++ b/www/~/%username/settings/index.html.spt @@ -88,14 +88,6 @@ emails = participant.get_emails() {{ _("Hide total giving to others.") }}