'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
COALESCE function returns the first of its arguments that is not NULL.
The effect here is to stamp all tips with the timestamp of the first
tip from this user to that. I believe this is used to determine the
order of transfers during payday.
The dict returned represents the row inserted in the tips table, with
an additional boolean indicating whether this is the first time this
tipper has tipped (we want to track that as part of our conversion
funnel).
This is the old Participant.set_tip_to method, migrated here to support
testing that still needs tips (take over, tip migration)'

def make_tip(self, tipper, tippee, amount, cursor=None):

    assert tipper.is_claimed
    if (not isinstance(tippee, Participant)):
        (tippee, u) = (Participant.from_username(tippee), tippee)
        if (not tippee):
            raise NoTippee(u)
    if (tipper.username == tippee.username):
        raise NoSelfTipping
    amount = Decimal(amount)
    if ((amount < MIN_TIP) or (amount > MAX_TIP)):
        raise BadAmount
    NEW_TIP = u'\n                                                INSERT    INTO    tips\n                                                                                                (ctime,    tipper,    tippee,    amount)\n                                                                    VALUES    (    COALESCE    ((    SELECT    ctime\n                                                                                                                                                                FROM    tips\n                                                                                                                                                            WHERE    (tipper=%(tipper)s    AND    tippee=%(tippee)s)\n                                                                                                                                                            LIMIT    1\n                                                                                                                                                        ),    CURRENT_TIMESTAMP)\n                                                                                                ,    %(tipper)s,    %(tippee)s,    %(amount)s\n                                                                                                    )\n                                                        RETURNING    *\n                                                                                        ,    (    SELECT    count(*)    =    0    FROM    tips    WHERE    tipper=%(tipper)s    )    AS    first_time_tipper\n\n                                '
    args = dict(tipper=tipper.username, tippee=tippee.username, amount=amount)
    t = (cursor or self.db).one(NEW_TIP, args)
    if (tippee.username == u'Gratipay'):
        tipper.update_is_free_rider((None if (amount == 0) else False), cursor)
    return t._asdict()
