66import pickle
77import pprint
88import urllib
9+ import logging
910import optparse
1011import requests
1112import subprocess
1516from pprint import pprint
1617from datetime import datetime
1718
19+ LOGGING_DISABELED = 100
20+ log_levels = [LOGGING_DISABELED , logging .CRITICAL , logging .ERROR ,
21+ logging .WARNING , logging .INFO , logging .DEBUG ]
22+ # Adapted from:
23+ # https://docs.python.org/2/howto/logging.html#configuring-logging
24+ # create logger
25+ logger = logging .getLogger (__name__ )
26+
27+ # create console handler and set level to debug
28+ ch = logging .StreamHandler ()
29+ ch .setLevel (logging .DEBUG )
30+
31+ # create formatter
32+ formatter = logging .Formatter ('%(asctime)s - %(name)s - %(levelname)s - %(message)s' )
33+
34+ # add formatter to ch
35+ ch .setFormatter (formatter )
36+
37+ # add ch to logger
38+ logger .addHandler (ch )
39+ logging .basicConfig (format = '%(asctime)s %(message)s' , datefmt = '%m/%d/%Y %I:%M:%S %p' )
40+
1841def split (total , num_people ):
1942 base = total * 100 // num_people / 100
2043 extra = total - num_people * base
@@ -23,9 +46,9 @@ def split(total, num_people):
2346 return base , extra
2447
2548class Splitwise :
26- def __init__ (self ):
27- if os .path .isfile ("oauth_client.pkl" ):
28- with open ('oauth_client.pkl' , 'rb' ) as oauth_pkl :
49+ def __init__ (self , options , args ):
50+ if os .path .isfile (options . api_client ):
51+ with open (options . api_client , 'rb' ) as oauth_pkl :
2952 self .client = pickle .load (oauth_pkl )
3053 else :
3154 self .get_client ()
@@ -99,44 +122,55 @@ def get_groups(self):
99122 def post_expense (self , uri ):
100123 resp = self .api_call (uri , 'POST' )
101124 if resp ["errors" ]:
102- print "URI:"
103- print uri
104- pprint (resp )
125+ sys . stderr . write ( "URI:" )
126+ sys . stderr . write ( uri )
127+ pprint (resp , stream = sys . stderr )
105128 else :
106129 sys .stdout .write ("." )
107130 sys .stdout .flush ()
108131
132+ class CsvSettings ():
133+ def __init__ (self , rows ):
134+ print "These are the first two rows of your csv"
135+ print '\n ' .join ([str (t ) for t in rows [0 :2 ]])
136+ print 'Colnum numbers start at 0'
137+ self .date_col = input ("Which column has the date?" )
138+ self .amount_col = input ("Which column has the amount?" )
139+ self .desc_col = input ("Which column has the description?" )
140+ self .has_title_row = raw_input ("Does first row have titles? [Y/n]" ).lower () != 'n'
141+ while True :
142+ try :
143+ self .local_currency = raw_input ("What currency were these transactions made in?" ).upper ()
144+ test = Money ("1.00" , self .local_currency ) #pylint: disable=W0612
145+ except ValueError as err :
146+ print err
147+ print "Try again..."
148+ else :
149+ break
150+ self .remember = raw_input ("Remember these settings? [Y/n]" ).lower () != 'n'
151+
152+ if self .remember :
153+ with open ("csv_settings.pkl" , "wb" ) as pkl :
154+ pickle .dump (self , pkl )
155+
109156class SplitGenerator ():
110157 def __init__ (self , options , args , api ):
111- self .api = api
112158 csv_file = args [0 ]
113159 group_name = args [1 ]
160+ self .api = api
161+ self .options = options
162+ self .args = args
114163 with open (csv_file , 'rb' ) as csvfile :
115164 reader = csv .reader (csvfile )
116165 self .rows = [x for x in reader ]
117-
118-
119- if os .path .isfile ("csv_settings.pkl" ):
120- with open ('csv_settings.pkl' , 'rb' ) as f :
121- self = pickle .load (f )
122-
166+
167+ if os .path .isfile (options .csv_settings ):
168+ with open (options .csv_settings , 'rb' ) as f :
169+ self .csv = pickle .load (f )
123170 else :
124- print "These are the first two rows of your csv"
125- print '\n ' .join ([str (t ) for t in self .rows [0 :2 ]])
126- print 'Colnum numbers start at 0'
127- self .date_col = input ("Which column has the date?" )
128- self .amount_col = input ("Which column has the amount?" )
129- self .desc_col = input ("Which column has the description?" )
130- self .has_title_row = raw_input ("Does first row have titles? [Y/n]" ).lower () != 'n'
131- self .local_currency = raw_input ("What currency were these transactions made in?" ).upper ()
132- self .test = Money ("1.00" , self .local_currency ) #pylint: disable=W0612
133- self .remember = raw_input ("Remember these settings? [Y/n]" ).lower () != 'n'
171+ self .csv = CsvSettings (self .rows )
134172
135- if self .remember :
136- with open ("csv_settings.pkl" , "wb" ) as pkl :
137- pickle .dump (self , pkl )
138-
139- if self .has_title_row :
173+ if self .csv .has_title_row :
140174 self .rows = self .rows [1 :]
141175
142176 self .make_transactions ()
@@ -145,10 +179,10 @@ def __init__(self, options, args, api):
145179 self .ask_for_splits ()
146180
147181 def make_transactions (self ):
148- self .transactions = [{"date" : datetime .strftime (datetime .strptime (r [self .date_col ], "%m/%d/%y" ), "%Y-%m-%dT%H:%M:%SZ" ),
149- "amount" : - 1 * Money (r [self .amount_col ], self .local_currency ),
150- "desc" : re .sub ('\s+' ,' ' , r [self .desc_col ])}
151- for r in self .rows if float (r [self .amount_col ]) < 0 ]
182+ self .transactions = [{"date" : datetime .strftime (datetime .strptime (r [self .csv . date_col ], "%m/%d/%y" ), "%Y-%m-%dT%H:%M:%SZ" ),
183+ "amount" : - 1 * Money (r [self .csv . amount_col ], self . csv .local_currency ),
184+ "desc" : re .sub ('\s+' ,' ' , r [self .csv . desc_col ])}
185+ for r in self .rows if float (r [self .csv . amount_col ]) < 0 ]
152186
153187 def get_group (self , name ):
154188 num_found = 0
@@ -173,12 +207,12 @@ def get_group(self, name):
173207
174208 def ask_for_splits (self ):
175209 for t in self .transactions :
176- if raw_input ("%s at %s $%s. Split? [y/N]" % (t ['date' ], t ['desc' ], t ['amount' ])).lower () == 'y' :
210+ if self . options . yes or raw_input ("%s at %s $%s. Split? [y/N]" % (t ['date' ], t ['desc' ], t ['amount' ])).lower () == 'y' :
177211 self .splits .append (t )
178212
179213 def __getitem__ (self , index ):
180214 s = self .splits [index ]
181- one_cent = Money ("0.01" , self .local_currency )
215+ one_cent = Money ("0.01" , self .csv . local_currency )
182216 num_people = len (self .members ) + 1
183217 base , extra = split (s ['amount' ], num_people )
184218 params = {
@@ -187,28 +221,36 @@ def __getitem__(self, index):
187221 "description" : s ["desc" ],
188222 "date" : s ["date" ],
189223 "group_id" : self .gid ,
190- "currency_code" : self .local_currency ,
224+ "currency_code" : self .csv . local_currency ,
191225 "users__0__user_id" : self .api .get_id (),
192226 "users__0__paid_share" : s ["amount" ].amount ,
193227 "users__0__owed_share" : base .amount ,
194228 }
195229 for i in range (len (self .members )):
196- params ['users__%s__user_id' % (i + 1 )] = sef .members [i ]
230+ params ['users__%s__user_id' % (i + 1 )] = self .members [i ]
197231 params ['users__%s__paid_share' % (i + 1 )] = 0
198- params ['users__%s__owed_share' % (i + 1 )] = (base + one_cent ).amount if extra else base .amount
232+ params ['users__%s__owed_share' % (i + 1 )] = (base + one_cent ).amount if extra . amount > 0 else base .amount
199233 extra -= one_cent
200234 paramsStr = urllib .urlencode (params )
201235 return "https://secure.splitwise.com/api/v3.0/create_expense?%s" % (paramsStr )
202236
203237
204238def main ():
205239 parser = optparse .OptionParser ()
206- parser .add_option ('-v' , '--verbose' , default = False , dest = 'verbose' )
240+ parser .add_option ('-v' , '--verbosity' , default = 2 , dest = 'verbosity' , help = 'change the logging level (0 - 6) default: 2' )
241+ parser .add_option ('-y' ,'' ,default = False , action = 'store_true' , dest = 'yes' , help = 'split all transactions in csv without confirmation' )
242+ parser .add_option ('-d' , '--dryrun' , default = False , action = 'store_true' , dest = 'dryrun' , help = 'prints requests instead of sending them' )
243+ parser .add_option ('' , '--csv-settings' , default = 'csv_settings.pkl' , dest = 'csv_settings' , help = 'supply different csv_settings object (for testing mostly)' )
244+ parser .add_option ('' , '--api-client' , default = 'oauth_client.pkl' , dest = 'api_client' , help = 'supply different splitwise api client (for testing mostly)' )
207245 options , args = parser .parse_args ()
208- splitwise = Splitwise ()
246+ logger .setLevel (log_levels [options .verbosity ])
247+ splitwise = Splitwise (options , args )
209248 split_gen = SplitGenerator (options , args , splitwise )
210249 print "Uploading splits"
211250 for uri in split_gen :
251+ if options .dryrun :
252+ print uri
253+ continue
212254 splitwise .post_expense (uri )
213255 sys .stdout .write ("\n " )
214256 sys .stdout .flush ()
0 commit comments