Closed chadwhitacre closed 7 years ago
I'm stuck thinking about the relationship between individuals, groups, and projects. I think #3337 is the cleanest way to go:
Users optionally have Projects. Projects optionally have Payroll.
Projects would be what you give money to.
But right now Users are what you give money to. We don't want to drop existing funding streams if we can at all help it. We want to give Users a way to migrate to a Project. Forcing migration before we process payments is okay since we're requiring acceptance of our new terms anyway.
I'm not sure how to pull off this pivot without #3337. That's a fair chunk of work.
Let's think about Participants (Users above) and Teams (Projects above).
Currently Teams are a kind of Participant (those with plural
number and > 0 takes
), but I think we should promote Teams to a first-class citizen with a new teams
table.
A Team is a group of people (some of whom may even be Participants) working together to deliver a given product or service.
Teams have Members (Participants of ours who are part of the Team). Some Members are Owners.
(Note that Participants can still be singular or plural.)
Right now, Tips attach to Participants. How do we migrate Tips from Participants to Teams? We basically need Participants to create a new Team, to which we will reattach their Tips.
Since giving to Participants has been ambiguous in regard to products or services delivered, migrating giving to a single-purpose Team will be a judgement call for our users. If I have 100 supporters, and 60 would say they were giving to me because of one product or service I provide, and 30 because of another, and 10 just because they love me, then how should I migrate? I don't even know who my supporters are, so it's not like I can manually review and make assignments ("Oh, this person is a Fizzbuzz user, that's probably why they've been supporting me. Let's move their payment to the new Fizzbuzz Team"). No: I have to guess at the 60-supporter product/service, and migrate all of my giving to a new Team dedicated to that. Then I can make a second Team for the 30-supporter product/service.
Can a Team give to another Team?
Can a Team give to another Team?
I think not, but I think the Owner of a Team can give to other Teams. Let's say that Exchange Routes still attach to Participants. The Owner of a Team is a Participant, and that's where the leftover money drains after Payroll, so we still have the chance to offset their giving with their receiving in a given cycle.
The approach I'm considering is to make new tables teams
, subscriptions
, payroll
, payments
that are parallel to participants
, tips
, takes
, transfers
. We no longer write to the old tables, but they're available to use for migrating. Migrating happens when a Participant takes the initiative; we're not migrating all tips
at once.
Where do we put Teams in our URL structure? And in our IA, for that matter?
Do we demote Participants? https://gratipay.com/whit537 → https://gratipay.com/~whit537. Does that redirect? Almost certainly, or we'd break lots of inbound links.
But then does https://gratipay.com/~Gratipay redirect back to https://gratipay.com/Gratipay? Do we have a single namespace for Participant and Team slugs, even if we differentiate them in the URL? Probably. It doesn't seem right to allow someone to register https://gratipay.com/~Gratipay. On the other hand, we're saying that we want an Owner Participant for a Team, so I guess we should allow ~Gratipay to be the Owner of Gratipay. Right?
In terms of IA, I think we're looking at the following changes as the absolute minimum to pull this off this week:
/$USERNAME/
→ /~$USERNAME/
$TEAM
at /$TEAM/
, with a Profile
at top level./$USERNAME/members/
→ /$TEAM/payroll/
.How do Participants register new Teams?
How about adding /new
for that? Then we add /dashboard/teams
for the new Teams review queue.
The first time a Participant with Tips creates a new Team, we should migrate all their Tips to Subscriptions for the Team (with prompting and confirmation, of course).
This feels good. I like how this is coming together, it's an evolution of Teams, a pretty natural step.
tips
& takes
with UI over subscriptions
& payroll
(#3400)subscriptions
& payroll
instead of tips
& takes
number
w/ takes
teams
want to be i18n'd like statementis_approved
tips.json
fake_data
After bashing around on #3400 all day yesterday, I'm starting to get a better idea of how to approach this. I've made three clean PRs:
We should be able to clean up tip limits based on number as well. Is there any other code we can simply cut out?
Beyond that, here's a review of work based on looking at a profile (hard, medium, easy):
anonymous_receiving
interacts with this but that's probably out of scope for this ticket.anonymous_receiving
.anonymous_giving
.What should we do with tips.json
? I think we should make a new API that uses team id instead, and just let tips.json go 404. How are we supposed to interpret tips to usernames from the old tipping system under the new system?
In which case, I think I'll rip out tips.json
for this week, and reticket bringing back something similar.
URL structure
/username/
to /~username/
(#3402)Reduce Surface Area
goal
(#3405)Rewrite Payday
subscriptions
, payroll
, and payments
tables_mixin_team
to a Team
classWire Up Teams
I have the feeling that I'm going to be doing most of this work myself. I'm not even sure we'll be able to wait for proper review before merging and deploying. I'm at least going to keep using PRs, even if I end up merging them myself.
I went ahead and merged/deployed #3402, to get the ball rolling.
From #3406, here's a census of the parts of the user experience that touch the tips
table:
Here's the details:
_check_tips
_check_orphans_no_tips
Participant.update_number
Participant.clear_tips_giving - close
Participant.clear_tips_receiving - close
Participant.update_giving - team takes, exchange routes, setting tips, take over
Participant.update_receiving - team takes, setting tips, take over
Participant.set_tip_to - {tip,tips}.json, close, throughout the test suite
Participant.get_tip_to - tip widget, old button widget, pricing
Participant.get_tip_distribution - stats, receiving page
Participant.get_giving_for_profile - giving page, tips.json, close
Participant.get_tips_receiving - unused!
Participant.get_current_tips - tips.json
Participant.take_over - take over
Participant.final_check - archive, close
www/about/stats.spt
www/about/tip-distribution.json.spt
I'm deploying #3403.
Looks like we have a regression from #3402: the tip widget is posting to /foo/tip.json
, which is 302 to a GET and the POST body is lost. I'm going to proceed with #3406, which simply makes tip.json
404 ahead of the move to /team/tip.json
.
Okay, I'm pumped. :boom: :exclamation:
#!/usr/bin/env python
"""Sandbox for exploring the new Payday algorithm.
"""
from __future__ import print_function, unicode_literals
from collections import defaultdict
# Classes
class _Thing(object):
def __init__(self, name):
self.name = name
self.values = list()
def __setitem__(self, k, v):
self.values.append((k,v))
def __repr__(self):
return(self.name)
def __str__(self):
return '\n'.join(['{} {}'.format(repr(k),v) for k,v in self.values])
class Participant(_Thing):
pass
class Team(_Thing):
owner = None
# Universe
a, b, c, d, e = [Participant(x) for x in 'abcde']
A, B, C, D, E = [Team(x) for x in 'ABCDE']
# subscriptions
a[A] = 1
a[B] = 1
a[C] = 1
a[E] = 1
b
c[A] = 1
c[C] = 1
c[E] = 1
d[D] = 1
e[D] = 1
# payroll
A[b] = 1
A[c] = 1
A.owner = c
B.owner = c
C.owner = a
D.owner = d
E[c] = 1
E.owner = e
def payday(participants, teams):
"""Given a list of participants and a list of teams, return a list.
The list we return contains instructions for funds transfer, both card
captures (positive) and bank deposits (negative).
"""
t_balances = defaultdict(int)
p_balances = defaultdict(int)
p_holding = defaultdict(int)
for p in participants:
for t, amount in p.values:
t_balances[t] += amount
p_holding[p] += amount
for t in teams:
for p, amount in t.values:
t_balances[t] -= amount
p_balances[p] += amount
for t in teams:
p_balances[t.owner] += t_balances[t]
t_balances[t] -= t_balances[t]
assert sum(t_balances.values()) == 0
return [(p, p_balances[p] - p_holding[p]) for p in participants]
for participant, instruction in payday([a,b,c,d,e], [A,B,C,D,E]):
print("{} {:2}".format(participant.name, instruction))
It looks like throwing a Team
abstraction into payday makes the algorithm much nicer. It seems like it will enable us to easily settle the graph completely each cycle, and have no escrow! Who knew!? :dancer:
Any guesses what the above spits out? :-)
(The "4" is wrong. It's zero, taking the sign into account [charge/deposit].)
@rohitpaulk Do you think there's any chance you'll be able to work on this before tomorrow's deadline? It'd be great to find a way to deal you in here. Maybe we can quickly stub out a teams
table and Team
class, and then you can work on the UI side while I work on the payday side?
@whit537 - Sure, sounds great. Let's cherry pick stuff out of #3400 and get a few more PRs out of the way.
Ah, looks like you're already on it :dancer:
Let's follow our common pattern - You start PRs, I'll build on them.
Let's follow our common pattern - You start PRs, I'll build on them.
Perfect. I actually have to disappear for an hour or so to get kids out the door. I see you've merged #3410. Can you start on wiring up the UI for creating teams? I've started #3412 for that.
!m @rohitpaulk
On it. !m @whit537
@rohitpaulk Hopefully you're topped up with #3412 and #3413 for a bit. I'm going to dive in on #3414. It occurs to me that we don't even need the ability to modify tips before tomorrow. I expect to develop the new payments-related tables on #3414. Either in #3412 or in a separate PR we can implement migration from the old tips
and takes
tables to the new tables, and then after Payday 154 we can start bringing back the UI to work with payments and payroll.
We have until 3:30pm US/Pacific tomorrow to complete payday. We do want to get a couple people signed up first, though. I'm still going to spend some time right now working on payday (#3414), because it's risky and challenging and I need to work on it in the morning while I'm fresh, but we can finish coding that tomorrow (I do have a 10:00 meeting with Citizens). We really should land the terms (#3408) and the team sign-up process (#3412) today if we can, in order to give @dcht00 @fenryxo @silverhook @ehmatthes @mixolidia et al. a little time to get in the door for tomorrow's payday (#3415).
cc: @copiesofcopies
Okay! We're making progress on the terms (#3408). We've got a batch of 12 small proposals/questions to sort out, after which we'll hit two slightly larger questions, one about Open Work, and another about how Teams register. How are we doing with #3412, @rohitpaulk?
Oh, right. You already told me it was ready for review. :-)
!m @rohitpaulk
I've topped up #3412. Next step is to move #3414 far enough forward that we have our new payments schema; #3412 is blocked on that and #3408. Less important but easier to tackle is #3413.
@rohitpaulk: If you come back around before I'm able to make significant progress on #3412 or #3414, I see two things you could work on:
I just made a pass through support and replied to people who have been waiting to hear from us, asking them to wait a little longer ...
Alright, I realized we don't need to block sign-ups on migration, only on terms. Here's the current state of the sign-up form:
It limits based on verified email and bank account or PayPal, and here's the success message (owner's email is displayed):
Alright, the pressure is on #3408. Once we land that we can start signing up Teams! After that it's all about #3414 (with a little bit of #3413 and some migration work, which could fall under #3415).
Awwwweeeesssssoooommmmeee.
!m @rohitpaulk
Made it through support. Now to land #3408 and #3412 ...
Okay! Applications for new teams are now open!
By "new teams" you mean anyone who wants to migrate, correct? Not just new people to the service.
I got error "400 Bad Request: You must attach a bank account or PayPal to apply for a new team." although ~NuvolaPlayer does have PayPal account attached. Should I create a new ticket for this?
I applied for introtopython.org. I used my personal Gratipay account to do so; I'm not sure if that was the correct approach, but I only have one Gratipay account.
Also, I see a message that my credit card is working but out of date. I tried to update the credit card, but the information didn't seem to save. I'm not sure where this fits in with all the recent changes.
By "new teams" you mean anyone who wants to migrate, correct? Not just new people to the service.
@mattbk Yes, that is correct.
I got error "400 Bad Request: You must attach a bank account or PayPal to apply for a new team." although ~NuvolaPlayer does have PayPal account attached.
Bah, sorry. Looking into it ...
I applied for introtopython.org.
Hooray! Introtopython.org is our first team! :dancer: !m @ehmatthes
I tried to update the credit card, but the information didn't seem to save.
Yeah, that's no doubt due to regressions from migrating to Braintree. We are starting to store new cards there even though we're going to still be processing on Balanced today. You can make a ticket if you want, or look for a recent one that might be related ...
Back from Citizens (#3366). Todo:
Last week, Gratipay stopped processing payments because of legal concerns with our old terms of service that became apparent after our processor, Balanced, announced that they're going out of business, and Stripe wouldn't take us. So in addition to migrating our processing infrastructure (#3377), we also have to change our business model at the same time. No :sweat:, right? :)
We hashed out our new business model on https://github.com/gratipay/inside.gratipay.com/issues/180 and https://github.com/gratipay/inside.gratipay.com/issues/192. This ticket is to track implementation. See also the Pivot milestone.
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.