<s> """<STR_LIT>""" <EOL> from __future__ import unicode_literals <EOL> import itertools <EOL> from multiprocessing . dummy import Pool as ThreadPool <EOL> import braintree <EOL> import aspen . utils <EOL> from aspen import log <EOL> from gratipay . billing . exchanges import ( <EOL> cancel_card_hold , capture_card_hold , create_card_hold , upcharge , MINIMUM_CHARGE , <EOL> ) <EOL> from gratipay . exceptions import NegativeBalance <EOL> from gratipay . models import check_db <EOL> from psycopg2 import IntegrityError <EOL> with open ( '<STR_LIT>' ) as f : <EOL> PAYDAY = f . read ( ) <EOL> class ExceptionWrapped ( Exception ) : pass <EOL> def threaded_map ( func , iterable , threads = <NUM_LIT:5> ) : <EOL> pool = ThreadPool ( threads ) <EOL> def g ( * a , ** kw ) : <EOL> try : <EOL> return func ( * a , ** kw ) <EOL> except Exception as e : <EOL> import traceback <EOL> raise ExceptionWrapped ( e , traceback . format_exc ( ) ) <EOL> try : <EOL> r = pool . map ( g , iterable ) <EOL> except ExceptionWrapped as e : <EOL> print ( e . args [ <NUM_LIT:1> ] ) <EOL> raise e . args [ <NUM_LIT:0> ] <EOL> pool . close ( ) <EOL> pool . join ( ) <EOL> return r <EOL> class NoPayday ( Exception ) : <EOL> __str__ = lambda self : "<STR_LIT>" <EOL> class Payday ( object ) : <EOL> """<STR_LIT>""" <EOL> @ classmethod <EOL> def start ( cls ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> d = cls . db . one ( """<STR_LIT>""" , back_as = dict ) <EOL> log ( "<STR_LIT>" ) <EOL> except IntegrityError : <EOL> d = cls . db . one ( """<STR_LIT>""" , back_as = dict ) <EOL> log ( "<STR_LIT>" ) <EOL> d [ '<STR_LIT>' ] = d [ '<STR_LIT>' ] . replace ( tzinfo = aspen . utils . utc ) <EOL> log ( "<STR_LIT>" % d [ '<STR_LIT>' ] ) <EOL> payday = Payday ( ) <EOL> payday . __dict__ . update ( d ) <EOL> return payday <EOL> def run ( self ) : <EOL> """<STR_LIT>""" <EOL> self . db . self_check ( ) <EOL> _start = aspen . utils . utcnow ( ) <EOL> log ( "<STR_LIT>" ) <EOL> if self . stage < <NUM_LIT:1> : <EOL> self . payin ( ) <EOL> self . mark_stage_done ( ) <EOL> if self . stage < <NUM_LIT:2> : <EOL> self . update_stats ( ) <EOL> self . mark_stage_done ( ) <EOL> self . end ( ) <EOL> self . notify_participants ( ) <EOL> _end = aspen . utils . utcnow ( ) <EOL> _delta = _end - _start <EOL> fmt_past = "<STR_LIT>" % _delta <EOL> log ( aspen . utils . to_age ( _start , fmt_past = fmt_past ) ) <EOL> def payin ( self ) : <EOL> """<STR_LIT>""" <EOL> with self . db . get_cursor ( ) as cursor : <EOL> self . prepare ( cursor ) <EOL> holds = self . create_card_holds ( cursor ) <EOL> self . process_payment_instructions ( cursor ) <EOL> self . transfer_takes ( cursor , self . ts_start ) <EOL> self . process_draws ( cursor ) <EOL> payments = cursor . all ( """<STR_LIT>""" , ( self . ts_start , ) ) <EOL> try : <EOL> self . settle_card_holds ( cursor , holds ) <EOL> self . update_balances ( cursor ) <EOL> check_db ( cursor ) <EOL> except : <EOL> import csv <EOL> from time import time <EOL> with open ( '<STR_LIT>' % time ( ) , '<STR_LIT:wb>' ) as f : <EOL> csv . writer ( f ) . writerows ( payments ) <EOL> raise <EOL> self . take_over_balances ( ) <EOL> @ staticmethod <EOL> def prepare ( cursor ) : <EOL> """<STR_LIT>""" <EOL> cursor . run ( PAYDAY ) <EOL> log ( '<STR_LIT>' ) <EOL> @ staticmethod <EOL> def fetch_card_holds ( participant_ids ) : <EOL> log ( '<STR_LIT>' ) <EOL> holds = { } <EOL> existing_holds = braintree . Transaction . search ( <EOL> braintree . TransactionSearch . status == '<STR_LIT>' <EOL> ) <EOL> for hold in existing_holds . items : <EOL> log_amount = hold . amount <EOL> p_id = int ( hold . custom_fields [ '<STR_LIT>' ] ) <EOL> if p_id in participant_ids : <EOL> log ( '<STR_LIT>' . format ( log_amount , p_id ) ) <EOL> holds [ p_id ] = hold <EOL> else : <EOL> cancel_card_hold ( hold ) <EOL> return holds <EOL> def create_card_holds ( self , cursor ) : <EOL> participants = cursor . all ( """<STR_LIT>""" ) <EOL> if not participants : <EOL> return { } <EOL> participant_ids = set ( p . id for p in participants ) <EOL> holds = self . fetch_card_holds ( participant_ids ) <EOL> def f ( p ) : <EOL> amount = p . giving_today - p . old_balance <EOL> if p . id in holds : <EOL> if amount >= MINIMUM_CHARGE : <EOL> charge_amount = upcharge ( amount ) [ <NUM_LIT:0> ] <EOL> if holds [ p . id ] . amount >= charge_amount : <EOL> return <EOL> else : <EOL> cancel_card_hold ( holds . pop ( p . id ) ) <EOL> else : <EOL> cancel_card_hold ( holds . pop ( p . id ) ) <EOL> return <EOL> if amount >= MINIMUM_CHARGE : <EOL> hold , error = create_card_hold ( self . db , p , amount ) <EOL> if error : <EOL> return <NUM_LIT:1> <EOL> else : <EOL> holds [ p . id ] = hold <EOL> threaded_map ( f , participants ) <EOL> if not holds : <EOL> return { } <EOL> cursor . run ( """<STR_LIT>""" , ( tuple ( holds . keys ( ) ) , ) ) <EOL> return holds <EOL> @ staticmethod <EOL> def process_payment_instructions ( cursor ) : <EOL> """<STR_LIT>""" <EOL> log ( "<STR_LIT>" ) <EOL> cursor . run ( "<STR_LIT>" ) <EOL> @ staticmethod <EOL> def transfer_takes ( cursor , ts_start ) : <EOL> return <EOL> cursor . run ( """<STR_LIT>""" , dict ( ts_start = ts_start ) ) <EOL> @ staticmethod <EOL> def process_draws ( cursor ) : <EOL> """<STR_LIT>""" <EOL> log ( "<STR_LIT>" ) <EOL> cursor . run ( "<STR_LIT>" ) <EOL> def settle_card_holds ( self , cursor , holds ) : <EOL> log ( "<STR_LIT>" ) <EOL> participants = cursor . all ( """<STR_LIT>""" ) <EOL> participants = [ p for p in participants if p . id in holds ] <EOL> log ( "<STR_LIT>" ) <EOL> def capture ( p ) : <EOL> amount = - p . new_balance <EOL> capture_card_hold ( self . db , p , amount , holds . pop ( p . id ) ) <EOL> threaded_map ( capture , participants ) <EOL> log ( "<STR_LIT>" % len ( participants ) ) <EOL> log ( "<STR_LIT>" ) <EOL> threaded_map ( cancel_card_hold , holds . values ( ) ) <EOL> log ( "<STR_LIT>" % len ( holds ) ) <EOL> @ staticmethod <EOL> def update_balances ( cursor ) : <EOL> log ( "<STR_LIT>" ) <EOL> participants = cursor . all ( """<STR_LIT>""" ) <EOL> for p in participants : <EOL> if p . new_balance < <NUM_LIT:0> and p . new_balance < p . cur_balance : <EOL> log ( p ) <EOL> raise NegativeBalance ( ) <EOL> cursor . run ( """<STR_LIT>""" ) <EOL> log ( "<STR_LIT>" % len ( participants ) ) <EOL> def take_over_balances ( self ) : <EOL> """<STR_LIT>""" <EOL> log ( "<STR_LIT>" ) <EOL> for i in itertools . count ( ) : <EOL> if i > <NUM_LIT:10> : <EOL> raise Exception ( '<STR_LIT>' ) <EOL> count = self . db . one ( """<STR_LIT>""" ) <EOL> if not count : <EOL> break <EOL> self . db . run ( """<STR_LIT>""" ) <EOL> def update_stats ( self ) : <EOL> log ( "<STR_LIT>" ) <EOL> self . db . run ( """<STR_LIT>""" , { '<STR_LIT>' : self . id } ) <EOL> log ( "<STR_LIT>" ) <EOL> def end ( self ) : <EOL> self . ts_end = self . db . one ( """<STR_LIT>""" , default = NoPayday ) . replace ( tzinfo = aspen . utils . utc ) <EOL> def notify_participants ( self ) : <EOL> log ( "<STR_LIT>" ) <EOL> ts_start , ts_end = self . ts_start , self . ts_end <EOL> exchanges = self . db . all ( """<STR_LIT>""" , locals ( ) ) <EOL> for e in exchanges : <EOL> if e . status not in ( '<STR_LIT>' , '<STR_LIT>' ) : <EOL> log ( '<STR_LIT>' % ( e . id , e . status ) ) <EOL> continue <EOL> i = <NUM_LIT:1> if e . status == '<STR_LIT>' else <NUM_LIT:2> <EOL> p = e . participant <EOL> if p . notify_charge & i == <NUM_LIT:0> : <EOL> continue <EOL> username = p . username <EOL> nteams , top_team = self . db . one ( """<STR_LIT>""" , locals ( ) ) <EOL> p . queue_email ( <EOL> '<STR_LIT>' + e . status , <EOL> exchange = dict ( id = e . id , amount = e . amount , fee = e . fee , note = e . note ) , <EOL> nteams = nteams , <EOL> top_team = top_team , <EOL> ) <EOL> def mark_stage_done ( self ) : <EOL> self . db . one ( """<STR_LIT>""" , default = NoPayday ) </s>
