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 })
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 } )
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 })
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 })