66import pickle
77import pprint
88import urllib
9+ import hashlib
910import logging
1011import optparse
1112import requests
@@ -45,6 +46,11 @@ def split(total, num_people):
4546 " something doesnt add up here: %d * %d + %d != %d" % (base , num_people , extra , total )
4647 return base , extra
4748
49+ def do_hash (msg ):
50+ m = hashlib .md5 ()
51+ m .update (msg )
52+ return m .hexdigest ()
53+
4854class Splitwise :
4955 def __init__ (self , api_client = 'oauth_client.pkl' ):
5056 if os .path .isfile (api_client ):
@@ -132,8 +138,12 @@ def post_expense(self, uri):
132138 def delete_expense (self , expense_id ):
133139 return self .api_call ("https://secure.splitwise.com/api/v3.0/delete_expense/%s" % expense_id , 'POST' )
134140
135- def get_expenses (self , limit = 0 ):
136- resp = self .api_call ("https://secure.splitwise.com/api/v3.0/get_expenses?limit=%s" % limit , 'GET' )
141+ def get_expenses (self , after_date = "" , limit = 0 , allow_deleted = True ):
142+ params = {'limit' : limit , "updated_after" : after_date }
143+ paramsStr = urllib .urlencode (params )
144+ resp = self .api_call ("https://secure.splitwise.com/api/v3.0/get_expenses?%s" % (paramsStr ), 'GET' )
145+ if not allow_deleted :
146+ resp ['expenses' ] = [exp for exp in resp ['expenses' ] if exp ['deleted_at' ] is None ]
137147 return resp ['expenses' ]
138148
139149class CsvSettings ():
@@ -145,6 +155,7 @@ def __init__(self, rows):
145155 self .amount_col = input ("Which column has the amount?" )
146156 self .desc_col = input ("Which column has the description?" )
147157 self .has_title_row = raw_input ("Does first row have titles? [Y/n]" ).lower () != 'n'
158+ self .newest_transaction = ''
148159 while True :
149160 try :
150161 self .local_currency = raw_input ("What currency were these transactions made in?" ).upper ()
@@ -155,11 +166,19 @@ def __init__(self, rows):
155166 else :
156167 break
157168 self .remember = raw_input ("Remember these settings? [Y/n]" ).lower () != 'n'
158-
169+
170+ def __del__ (self ):
159171 if self .remember :
160172 with open ("csv_settings.pkl" , "wb" ) as pkl :
161173 pickle .dump (self , pkl )
162174
175+ def record_newest_transaction (self , rows ):
176+ if self .has_title_row :
177+ self .newest_transaction = do_hash (str (rows [1 ]))
178+ else :
179+ self .newest_transaction = do_hash (str (rows [0 ]))
180+
181+
163182class SplitGenerator ():
164183 def __init__ (self , options , args , api ):
165184 csv_file = args [0 ]
@@ -176,20 +195,26 @@ def __init__(self, options, args, api):
176195 self .csv = pickle .load (f )
177196 else :
178197 self .csv = CsvSettings (self .rows )
179-
198+
180199 if self .csv .has_title_row :
181200 self .rows = self .rows [1 :]
182201
183202 self .make_transactions ()
203+ self .csv .record_newest_transaction (self .rows )
184204 self .get_group (group_name )
185205 self .splits = []
186206 self .ask_for_splits ()
187207
188208 def make_transactions (self ):
189- self .transactions = [{"date" : datetime .strftime (datetime .strptime (r [self .csv .date_col ], "%m/%d/%y" ), "%Y-%m-%dT%H:%M:%SZ" ),
190- "amount" : - 1 * Money (r [self .csv .amount_col ], self .csv .local_currency ),
191- "desc" : re .sub ('\s+' ,' ' , r [self .csv .desc_col ])}
192- for r in self .rows if float (r [self .csv .amount_col ]) < 0 ]
209+ self .transactions = []
210+ for r in self .rows :
211+ if not self .options .try_all and do_hash (str (r )) == self .csv .newest_transaction :
212+ break
213+ if float (r [self .csv .amount_col ]) < 0 :
214+ self .transactions .append ({"date" : datetime .strftime (datetime .strptime (r [self .csv .date_col ], "%m/%d/%y" ), "%Y-%m-%dT%H:%M:%SZ" ),
215+ "amount" : - 1 * Money (r [self .csv .amount_col ], self .csv .local_currency ),
216+ "desc" : re .sub ('\s+' ,' ' , r [self .csv .desc_col ])}
217+ )
193218
194219 def get_group (self , name ):
195220 num_found = 0
@@ -249,6 +274,7 @@ def main():
249274 parser .add_option ('-d' , '--dryrun' , default = False , action = 'store_true' , dest = 'dryrun' , help = 'prints requests instead of sending them' )
250275 parser .add_option ('' , '--csv-settings' , default = 'csv_settings.pkl' , dest = 'csv_settings' , help = 'supply different csv_settings object (for testing mostly)' )
251276 parser .add_option ('' , '--api-client' , default = 'oauth_client.pkl' , dest = 'api_client' , help = 'supply different splitwise api client (for testing mostly)' )
277+ parser .add_option ('-a' , '--all' , default = False , action = 'store_true' , dest = 'try_all' , help = 'consider all transactions in csv file no matter whether they were already seen' )
252278 options , args = parser .parse_args ()
253279 logger .setLevel (log_levels [options .verbosity ])
254280 splitwise = Splitwise (options .api_client )
0 commit comments