Exemple #1
0
    def suggest_loan_ids(self,
                         resp_name,
                         itgs,
                         comment_fullname,
                         lender_username,
                         loan_id,
                         amt,
                         rpiden,
                         rpversion,
                         loan=None):
        loans = Table('loans')
        lenders = Table('lenders')
        itgs.write_cursor.execute(
            loan_format_helper.create_loans_query().where(
                lenders.username == Parameter('%s')).where(
                    loans.repaid_at.isnull()).orderby(
                        loans.created_at, order=Order.desc).limit(7).get_sql(),
            (lender_username.lower(), ))

        loans = []
        row = itgs.write_cursor.fetchone()
        while row is not None:
            loans.append(loan_format_helper.fetch_loan(row))
            row = itgs.write_cursor.fetchone()

        suggested_loans = loan_format_helper.format_loan_table(loans,
                                                               include_id=True)

        formatted_response = get_response(
            itgs,
            resp_name,
            lender_username=lender_username,
            loan_id=loan_id,
            amount=str(amt),
            loan=('Loan Not Available' if loan is None
                  else loan_format_helper.format_loan_table([loan],
                                                            include_id=True)),
            suggested_loans=suggested_loans)
        utils.reddit_proxy.send_request(itgs, rpiden, rpversion,
                                        'post_comment', {
                                            'parent': comment_fullname,
                                            'text': formatted_response
                                        })
Exemple #2
0
    def handle_comment(self, itgs, comment, rpiden, rpversion):
        token_vals = PARSER.parse(comment['body'])
        lender_username = comment['author']
        borrower_username = token_vals[0]
        amt = token_vals[1]

        comment_permalink = 'https://www.reddit.com/comments/{}/redditloans/{}'.format(
            comment['link_fullname'][3:], comment['fullname'][3:])

        loans = Table('loans')
        lenders = Table('lenders')
        borrowers = Table('borrowers')

        effected_loans_pre = []
        effected_loans_post = []
        remaining = amt
        while remaining.minor > 0:
            itgs.write_cursor.execute(
                loan_format_helper.create_loans_query().where(
                    lenders.username == Parameter('%s')).where(
                        borrowers.username == Parameter('%s')).where(
                            loans.repaid_at.isnull()).orderby(
                                loans.created_at,
                                order=Order.asc).limit(1).get_sql(),
                (lender_username.lower(), borrower_username.lower()))
            row = itgs.write_cursor.fetchone()
            if row is None:
                break
            loan_pre = loan_format_helper.fetch_loan(row)
            old_minor = remaining.minor
            (_, _, remaining) = utils.paid_utils.apply_repayment(
                itgs, loan_pre.id, remaining)
            itgs.write_cursor.execute(
                loan_format_helper.create_loans_query().where(
                    loans.id == Parameter('%s')).get_sql(), (loan_pre.id, ))
            row = itgs.write_cursor.fetchone()
            if row is None:
                itgs.logger.print(
                    Level.WARN,
                    'Somehow, while handling the paid summon by /u/{} at {}, '
                    +
                    'the loan was deleted while applying repayment. We stopped '
                    +
                    'propagating the loan early. If nobody was deleting loans this '
                    + 'is definitely developer error.', lender_username,
                    comment_permalink)
                effected_loans_pre.append(loan_pre)
                break
            loan_post = loan_format_helper.fetch_loan(row)
            if old_minor <= remaining.minor:
                # Sanity check to prevent loops
                break
            effected_loans_pre.append(loan_pre)
            effected_loans_post.append(loan_post)

        itgs.logger.print(
            Level.INFO, '/u/{} was repaid by /u/{} by {} over {} loan{} at {}',
            lender_username, borrower_username, amt, len(effected_loans_pre),
            's' if len(effected_loans_pre) != 1 else '', comment_permalink)

        formatted_response = get_response(
            itgs,
            'paid',
            lender_username=lender_username,
            borrower_username=borrower_username,
            loans_before=loan_format_helper.format_loan_table(
                effected_loans_pre),
            loans_after=loan_format_helper.format_loan_table(
                effected_loans_post),
            num_loans_affected=len(effected_loans_pre),
            amount=str(amt),
            remaining=str(remaining))

        utils.reddit_proxy.send_request(itgs, rpiden, rpversion,
                                        'post_comment', {
                                            'parent': comment['fullname'],
                                            'text': formatted_response
                                        })
def handle_loan_request(version, event):
    """Handle a loan request event from the events queue.

    Arguments:
        version (any): The version to pass to the reddit proxy
        event (dict): Describes the request
            post (dict):
                A self post from reddit-proxy "subreddit_links" (Documented
                at reddit-proxy/src/handlers/links.py)
            request (dict):
                A dictified utils.req_post_interpreter.LoanRequest
    """
    post = event['post']
    with LazyIntegrations(logger_iden='runners/borrower_request.py#handle_loan_request') as itgs:
        itgs.logger.print(
            Level.TRACE,
            'Detected loan request from /u/{}',
            post['author']
        )

        users = Table('users')
        itgs.read_cursor.execute(
            users.select(users.id)
            .where(users.username == Parameter('%s'))
            .get_sql(),
            (post['author'].lower(),)
        )
        row = itgs.read_cursor.fetchone()
        if row is None:
            itgs.logger.print(
                Level.TRACE,
                'Ignoring loan request from /u/{} - they do not have any '
                + 'outstanding loans (no history)',
                post['author']
            )
            return
        (author_user_id,) = row

        loans = Table('loans')
        itgs.read_cursor.execute(
            loan_format_helper.create_loans_query()
            .select(loans.lender_id)
            .where(loans.borrower_id == Parameter('%s'))
            .where(loans.repaid_at.isnull())
            .where(loans.unpaid_at.isnull())
            .get_sql(),
            (author_user_id,)
        )
        row = itgs.read_cursor.fetchone()
        outstanding_borrowed_loans = []
        while row is not None:
            outstanding_borrowed_loans.append({
                'pretty': loan_format_helper.fetch_loan(row[:-1]),
                'lender_id': row[-1]
            })
            row = itgs.read_cursor.fetchone()

        if not outstanding_borrowed_loans:
            itgs.logger.print(
                Level.TRACE,
                'Ignoring loan request from /u/{} - no outstanding loans',
                post['author']
            )
            return

        unique_lenders = frozenset(loan['lender_id'] for loan in outstanding_borrowed_loans)
        itgs.logger.print(
            Level.INFO,
            '/u/{} made a loan request while they have {} open loans from '
            + '{} unique lenders: {}. Going to inform each lender which has not '
            + 'opted out of borrower request pms.',
            post['author'], len(outstanding_borrowed_loans), len(unique_lenders),
            unique_lenders
        )

        for lender_id in unique_lenders:
            lender_settings = get_settings(itgs, lender_id)
            if lender_settings.borrower_req_pm_opt_out:
                itgs.logger.print(
                    Level.TRACE,
                    'Not sending an alert to user {} - opted out',
                    lender_id
                )
                continue

            pretty_loans = [
                loan['pretty']
                for loan in outstanding_borrowed_loans
                if loan['lender_id'] == lender_id
            ]

            formatted_body = get_response(
                itgs,
                'borrower_request',
                lender_username=pretty_loans[0].lender,
                borrower_username=post['author'],
                thread='https://www.reddit.com/r/{}/comments/{}/redditloans'.format(
                    post['subreddit'], post['fullname'][3:]
                ),
                loans=loan_format_helper.format_loan_table(pretty_loans, include_id=True)
            )

            utils.reddit_proxy.send_request(
                itgs, 'borrower_request', version, 'compose',
                {
                    'recipient': pretty_loans[0].lender,
                    'subject': '/u/{} has made a request thread'.format(post['author']),
                    'body': formatted_body
                }
            )
Exemple #4
0
    def handle_comment(self, itgs, comment, rpiden, rpversion):
        token_vals = PARSER.parse(comment['body'])
        lender_username = comment['author']
        borrower_username = token_vals[0]

        comment_permalink = 'https://www.reddit.com/comments/{}/redditloans/{}'.format(
            comment['link_fullname'][3:], comment['fullname'][3:])

        loans = Table('loans')
        lenders = Table('lenders')
        borrowers = Table('borrowers')
        itgs.write_cursor.execute(
            loan_format_helper.create_loans_query().where(
                lenders.username == Parameter('%s')).where(
                    borrowers.username == Parameter('%s')).where(
                        loans.unpaid_at.isnull()).where(
                            loans.repaid_at.isnull()).get_sql(),
            (lender_username.lower(), borrower_username.lower()))
        row = itgs.write_cursor.fetchone()

        affected_pre = []
        while row is not None:
            affected_pre.append(loan_format_helper.fetch_loan(row))
            row = itgs.write_cursor.fetchone()

        if affected_pre:
            itgs.write_cursor.execute(
                Query.update(loans).set(loans.unpaid_at, Now()).where(
                    loans.id.isin([Parameter('%s')
                                   for _ in affected_pre])).get_sql(),
                tuple(loan.id for loan in affected_pre))

            loan_unpaid_events = Table('loan_unpaid_events')
            itgs.write_cursor.execute(
                Query.into(loan_unpaid_events).columns(
                    loan_unpaid_events.loan_id,
                    loan_unpaid_events.unpaid).insert(
                        *[(Parameter('%s'), True)
                          for _ in affected_pre]).returning(
                              loan_unpaid_events.id).get_sql(),
                tuple(loan.id for loan in affected_pre))
            itgs.channel.exchange_declare('events', 'topic')
            row = itgs.write_cursor.fetchone()
            while row is not None:
                itgs.channel.basic_publish(
                    'events', 'loans.unpaid',
                    json.dumps({"loan_unpaid_event_id": row[0]}))
                row = itgs.write_cursor.fetchone()

            itgs.write_cursor.execute(
                loan_format_helper.create_loans_query().where(
                    loans.id.isin([Parameter('%s')
                                   for _ in affected_pre])).get_sql(),
                tuple(loan.id for loan in affected_pre))
            row = itgs.write_cursor.fetchone()
            affected_post = []
            while row is not None:
                affected_post.append(loan_format_helper.fetch_loan(row))
                row = itgs.write_cursor.fetchone()
        else:
            affected_post = []

        itgs.logger.print(Level.INFO,
                          '/u/{} marked {} loan{} sent to /u/{} unpaid at {}',
                          lender_username, len(affected_pre),
                          's' if len(affected_pre) != 1 else '',
                          borrower_username, comment_permalink)

        borrower_summary = loan_format_helper.get_and_format_all_or_summary(
            itgs, borrower_username)

        formatted_response = get_response(
            itgs,
            'unpaid',
            lender_username=lender_username,
            borrower_username=borrower_username,
            loans_before=loan_format_helper.format_loan_table(affected_pre),
            loans_after=loan_format_helper.format_loan_table(affected_post),
            borrower_summary=borrower_summary)

        utils.reddit_proxy.send_request(itgs, rpiden, rpversion,
                                        'post_comment', {
                                            'parent': comment['fullname'],
                                            'text': formatted_response
                                        })
Exemple #5
0
    def handle_comment(self, itgs, comment, rpiden, rpversion):
        token_vals = PARSER.parse(comment['body'])
        lender_username = comment['author']
        loan_id = token_vals[0]
        amt = token_vals[1]

        comment_permalink = 'https://www.reddit.com/comments/{}/redditloans/{}'.format(
            comment['link_fullname'][3:], comment['fullname'][3:])

        loans = Table('loans')
        itgs.write_cursor.execute(
            loan_format_helper.create_loans_query().where(
                loans.id == Parameter('%s')).get_sql(), (loan_id, ))
        row = itgs.write_cursor.fetchone()
        if row is None:
            itgs.logger.print(
                Level.INFO,
                '/u/{} tried to mark non-existent loan {} as paid at {}',
                lender_username, loan_id, comment_permalink)
            self.suggest_loan_ids('paid_with_id_not_found', itgs,
                                  comment['fullname'], lender_username,
                                  loan_id, amt, rpiden, rpversion)
            return

        loan = loan_format_helper.fetch_loan(row)
        if loan.lender.lower() != lender_username.lower():
            itgs.logger.print(
                Level.INFO,
                '/u/{} tried to mark loan {} (lender: {}, borrower: {}) as paid at {}',
                lender_username, loan_id, loan.lender, loan.borrower,
                comment_permalink)
            self.suggest_loan_ids('paid_with_id_wrong_lender',
                                  itgs,
                                  comment['fullname'],
                                  lender_username,
                                  loan_id,
                                  amt,
                                  rpiden,
                                  rpversion,
                                  loan=loan)
            return

        if loan.repaid_at is not None:
            itgs.logger.print(
                Level.INFO,
                '/u/{} tried to mark loan {} (already repaid) as paid at {}',
                lender_username, loan_id, comment_permalink)
            self.suggest_loan_ids('paid_with_id_already_repaid',
                                  itgs,
                                  comment['fullname'],
                                  lender_username,
                                  loan_id,
                                  amt,
                                  rpiden,
                                  rpversion,
                                  loan=loan)
            return

        (_, applied,
         remaining) = utils.paid_utils.apply_repayment(itgs, loan_id, amt)

        itgs.write_cursor.execute(
            loan_format_helper.create_loans_query().where(
                loans.id == Parameter('%s')).get_sql(), (loan_id, ))
        loan_after = loan_format_helper.fetch_loan(
            itgs.write_cursor.fetchone())

        itgs.logger.print(
            Level.INFO,
            '/u/{} repaid /u/{} {} ({} ignored) toward loan {} - permalink: {}',
            loan.borrower, lender_username, applied, remaining, loan_id,
            comment_permalink)

        formatted_response = get_response(
            itgs,
            'paid_with_id',
            loan_id=loan.id,
            lender_username=lender_username,
            borrower_username=loan.borrower,
            loan_before=loan_format_helper.format_loan_table([loan],
                                                             include_id=True),
            loan_after=loan_format_helper.format_loan_table([loan_after],
                                                            include_id=True),
            amount=str(amt),
            applied=str(applied),
            remaining=str(remaining))

        utils.reddit_proxy.send_request(itgs, rpiden, rpversion,
                                        'post_comment', {
                                            'parent': comment['fullname'],
                                            'text': formatted_response
                                        })