1- #! /usr/bin/python
2-
3- import requests
1+ import os
42import csv
53import sys
6- import oauthlib .oauth1
7- import pprint
8- import webbrowser
9- import subprocess
10- import os
11- import pickle
124import json
5+ import pickle
6+ import pprint
137import urllib
8+ import requests
9+ import subprocess
10+ import webbrowser
11+ import oauthlib .oauth1
12+ from money import Money
1413from pprint import pprint
1514from datetime import datetime
1615
17- if os .path .isfile ("consumer_oauth.json" ):
18- with open ("consumer_oauth.json" , 'rb' ) as f :
19- consumer = json .load (f )
20- ckey = consumer ['consumer_key' ]
21- csecret = consumer ['consumer_secret' ]
22- else :
23- with open ("consumer_oauth.json" , 'wb' ) as f :
24- json .dump ({'consumer_key' :'YOUR KEY HERE' ,
25- 'consumer_secret' :'YOUR SECRET HERE' }, f )
26- exit ("go to https://secure.splitwise.com/oauth_clients to obtain your keys. place them in consumer_oauth.json" )
16+ def get_client_auth ():
17+ if os .path .isfile ("consumer_oauth.json" ):
18+ with open ("consumer_oauth.json" , 'rb' ) as oauth_file :
19+ consumer = json .load (oauth_file )
20+ ckey = consumer ['consumer_key' ]
21+ csecret = consumer ['consumer_secret' ]
22+ else :
23+ with open ("consumer_oauth.json" , 'wb' ) as oauth_file :
24+ json .dump ({'consumer_key' :'YOUR KEY HERE' ,
25+ 'consumer_secret' :'YOUR SECRET HERE' }, oauth_file )
26+ exit ("go to https://secure.splitwise.com/oauth_clients to obtain your keys." +
27+ "place them in consumer_oauth.json" )
28+ return ckey , csecret
2729
2830def get_client ():
29- client = oauthlib .oauth1 .Client ( ckey , client_secret = csecret )
30- uri , headers , body = client .sign ("https://secure.splitwise.com/api/v3.0/get_request_token" , http_method = 'POST' )
31+ ckey , csecret = get_client_auth ()
32+ client = oauthlib .oauth1 .Client (ckey , client_secret = csecret )
33+ uri , headers , body = client .sign ("https://secure.splitwise.com/api/v3.0/get_request_token" ,
34+ http_method = 'POST' )
3135 r = requests .post (uri , headers = headers , data = body )
3236 resp = r .text .split ('&' )
3337 oauth_token = resp [0 ].split ('=' )[1 ]
@@ -36,114 +40,140 @@ def get_client():
3640
3741 webbrowser .open_new (uri )
3842
39- p = subprocess .Popen (['./server.py' ], stdout = subprocess .PIPE , shell = True )
40- stdout , stderr = p .communicate ()
43+ proc = subprocess .Popen (['./server.py' ], stdout = subprocess .PIPE , shell = True )
44+ stdout , stderr = proc .communicate ()
45+ if stderr :
46+ exit (stderr )
4147 client = oauthlib .oauth1 .Client (ckey , client_secret = csecret ,
42- resource_owner_key = oauth_token , resource_owner_secret = oauth_secret ,
48+ resource_owner_key = oauth_token ,
49+ resource_owner_secret = oauth_secret ,
4350 verifier = stdout .strip ())
4451
45- uri , headers , body = client .sign ("https://secure.splitwise.com/api/v3.0/get_access_token" , http_method = 'POST' )
46- r = requests .post (uri , headers = headers , data = body )
47- resp = r .text .split ('&' )
48- oauth_token = resp [0 ].split ('=' )[1 ]
49- oauth_secret = resp [1 ].split ('=' )[1 ]
52+ uri , headers , body = client .sign ("https://secure.splitwise.com/api/v3.0/get_access_token" ,
53+ http_method = 'POST' )
54+ resp = requests .post (uri , headers = headers , data = body )
55+ tokens = resp .text .split ('&' )
56+ oauth_token = tokens [0 ].split ('=' )[1 ]
57+ oauth_secret = tokens [1 ].split ('=' )[1 ]
5058 client = oauthlib .oauth1 .Client (ckey , client_secret = csecret ,
51- resource_owner_key = oauth_token , resource_owner_secret = oauth_secret ,
59+ resource_owner_key = oauth_token ,
60+ resource_owner_secret = oauth_secret ,
5261 verifier = stdout .strip ())
53- with open ('oauth_client.pkl' , 'wb' ) as f :
54- pickle .dump (client , f )
62+ with open ('oauth_client.pkl' , 'wb' ) as pkl :
63+ pickle .dump (client , pkl )
5564 return client
5665
57-
58- if os .path .isfile ("oauth_client.pkl" ):
59- with open ('oauth_client.pkl' , 'rb' ) as f :
60- client = pickle .load (f )
61- else :
62- client = get_client ()
63-
64- #uri, headers, body = client.sign("https://secure.splitwise.com/api/v3.0/get_expenses?limit=3", http_method='GET')
65- #r = requests.get(uri, headers=headers, data=body)
66- #r = json.loads(r.text)
67-
68- uri , headers , body = client .sign ("https://secure.splitwise.com/api/v3.0/get_current_user" , http_method = 'GET' )
69- r = requests .get (uri , headers = headers , data = body )
70- r = json .loads (r .text )
71- myId = r ['user' ]['id' ]
72-
73- uri , headers , body = client .sign ("https://secure.splitwise.com/api/v3.0/get_groups" , http_method = 'GET' )
74- r = requests .get (uri , headers = headers , data = body )
75- r = json .loads (r .text )
76- numFound = 0
77- gid = ''
78- members = {}
79- for group in r ['groups' ]:
80- if group ['name' ].lower () == sys .argv [2 ].lower ():
81- gid = group ['id' ]
82- members = [ m ['id' ] for m in group ['members' ] if m ['id' ] != myId ]
83- numFound += 1
84-
85- print members
86-
87- if numFound > 1 :
88- exit ("More than 1 group found" )
89- elif numFound < 1 :
90- exit ("No matching group" )
91- elif len ( members ) < 1 :
92- exit ("No members in group" )
93- with open (sys .argv [1 ], 'rb' ) as csvfile :
94- reader = csv .reader (csvfile )
95- rows = [ x for x in reader ]
96-
97-
98- print "These are the first two rows of your csv"
99- print '\n ' .join ( [ str (t ) for t in rows [0 :2 ] ] )
100- print 'Colnum numbers start at 0'
101- dateCol = input ("Which column has the date?" )
102- amountCol = input ("Which column has the amount?" )
103- descCol = input ("Which column has the description?" )
104- titleRow = raw_input ("Does first row have titles? [Y/n]" ).lower () != 'n'
105- if titleRow :
106- rows = rows [1 :]
107- transactions = [ { "date" :datetime .strftime (datetime .strptime (r [dateCol ],"%m/%d/%y" ), "%Y-%m-%dT%H:%M:%SZ" ),
108- "amount" :- 1 * float (r [amountCol ]),
109- "desc" :r [descCol ] }
110- for r in rows if float (r [amountCol ]) < 0 ]
111- splits = []
112- for t in transactions :
113- if raw_input ("%s at %s $%s. Split? [y/N]" % (t ['date' ], t ['desc' ], t ['amount' ])).lower () == 'y' :
114- splits .append (t )
115-
116-
117-
118- for s in splits :
119- numPeople = len (members ) + 1
120- totalCents = int (s ['amount' ] * 100 )
121- baseAmount = totalCents // numPeople
122- extraCents = totalCents - numPeople * baseAmount
123- params = {
124- "payment" : 'false' ,
125- "cost" : s ["amount" ],
126- "description" : s ["desc" ],
127- "date" : s ["date" ],
128- "group_id" :gid ,
129- "users__0__user_id" :myId ,
130- "users__0__paid_share" :s ["amount" ],
131- "users__0__owed_share" :baseAmount / 100.0 ,
132- }
133- for i in range (len (members )):
134- params ['users__%s__user_id' % (i + 1 )] = members [i ]
135- params ['users__%s__paid_share' % (i + 1 )] = 0
136- params ['users__%s__owed_share' % (i + 1 )] = (baseAmount + 1 )/ 100.0 if extraCents else baseAmount / 100.0
137- extraCents -= 1
138- params = urllib .urlencode (params )
139- uri , headers , body = client .sign ("https://secure.splitwise.com/api/v3.0/create_expense?%s" % params , http_method = 'POST' )
140- print "Request"
141- print uri
142- print "Headers:"
143- print headers
144- print "Body:"
145- print body
146- r = requests .post (uri , headers = headers , data = params )
147- resp = json .loads (r .text )
148- print "Response:"
149- pprint (resp )
66+ def split (total , num_people ):
67+ base = total * 100 // num_people / 100
68+ extra = total - num_people * base
69+ assert base * num_people + extra == total , "InternalError:" + \
70+ " something doesnt add up here: %d * %d + %d != %d" % (base , num_people , extra , total )
71+ return base , extra
72+
73+ def api_call (url , http_method , client ):
74+ uri , headers , body = client .sign (url , http_method = http_method )
75+ resp = requests .request (http_method , uri , headers = headers , data = body )
76+ return resp .json ()
77+
78+ def main ():
79+ if os .path .isfile ("oauth_client.pkl" ):
80+ with open ('oauth_client.pkl' , 'rb' ) as oauth_pkl :
81+ client = pickle .load (oauth_pkl )
82+ else :
83+ client = get_client ()
84+
85+ resp = api_call ("https://secure.splitwise.com/api/v3.0/get_current_user" , 'GET' , client )
86+ my_id = resp ['user' ]['id' ]
87+
88+ resp = api_call ("https://secure.splitwise.com/api/v3.0/get_groups" , 'GET' , client )
89+ num_found = 0
90+ gid = ''
91+ members = {}
92+
93+ for group in resp ['groups' ]:
94+ if group ['name' ].lower () == sys .argv [2 ].lower ():
95+ gid = group ['id' ]
96+ members = [m ['id' ] for m in group ['members' ] if m ['id' ] != my_id ]
97+ num_found += 1
98+
99+ if num_found > 1 :
100+ exit ("More than 1 group found" )
101+ elif num_found < 1 :
102+ exit ("No matching group" )
103+ elif len (members ) < 1 :
104+ exit ("No members in group" )
105+
106+ with open (sys .argv [1 ], 'rb' ) as csvfile :
107+ reader = csv .reader (csvfile )
108+ rows = [x for x in reader ]
109+
110+
111+ if os .path .isfile ("csv_settings.pkl" ):
112+ with open ('csv_settings.pkl' , 'rb' ) as f :
113+ date_col , amount_col , desc_col , has_title_row , local_currency , remember = pickle .load (f )
114+
115+ else :
116+ print "These are the first two rows of your csv"
117+ print '\n ' .join ([str (t ) for t in rows [0 :2 ]])
118+ print 'Colnum numbers start at 0'
119+ date_col = input ("Which column has the date?" )
120+ amount_col = input ("Which column has the amount?" )
121+ desc_col = input ("Which column has the description?" )
122+ has_title_row = raw_input ("Does first row have titles? [Y/n]" ).lower () != 'n'
123+ local_currency = raw_input ("What currency were these transactions made in?" )
124+ test = Money ("1.00" , local_currency ) #pylint: disable=W0612
125+ remember = raw_input ("Remember these settings? [Y/n]" ).lower () != 'n'
126+
127+ if remember :
128+ with open ("csv_settings.pkl" , "wb" ) as pkl :
129+ csv_settings = (date_col , amount_col , desc_col , has_title_row , local_currency , remember )
130+ pickle .dump (csv_settings , pkl )
131+
132+ if has_title_row :
133+ rows = rows [1 :]
134+ transactions = [{"date" :datetime .strftime (datetime .strptime (r [date_col ], "%m/%d/%y" ), "%Y-%m-%dT%H:%M:%SZ" ),
135+ "amount" :- 1 * Money (r [amount_col ], local_currency ),
136+ "desc" :r [desc_col ]}
137+ for r in rows if float (r [amount_col ]) < 0 ]
138+ splits = []
139+ for t in transactions :
140+ if raw_input ("%s at %s $%s. Split? [y/N]" % (t ['date' ], t ['desc' ], t ['amount' ])).lower () == 'y' :
141+ splits .append (t )
142+
143+
144+ print "Uploading %d splits" % len (splits )
145+ one_cent = Money ("0.01" , local_currency )
146+ for s in splits :
147+ num_people = len (members ) + 1
148+ base , extra = split (s ['amount' ], num_people )
149+ params = {
150+ "payment" : 'false' ,
151+ "cost" : s ["amount" ].amount ,
152+ "description" : s ["desc" ],
153+ "date" : s ["date" ],
154+ "group_id" : gid ,
155+ "currency_code" : local_currency ,
156+ "users__0__user_id" : my_id ,
157+ "users__0__paid_share" : s ["amount" ].amount ,
158+ "users__0__owed_share" : base .amount ,
159+ }
160+ for i in range (len (members )):
161+ params ['users__%s__user_id' % (i + 1 )] = members [i ]
162+ params ['users__%s__paid_share' % (i + 1 )] = 0
163+ params ['users__%s__owed_share' % (i + 1 )] = (base + one_cent ).amount if extra else base .amount
164+ extra -= one_cent
165+ paramsStr = urllib .urlencode (params )
166+ uri = "https://secure.splitwise.com/api/v3.0/create_expense?%s" % (paramsStr )
167+ resp = api_call (uri , 'POST' , client )
168+ if resp ["errors" ]:
169+ print "URI:"
170+ print uri
171+ pprint (resp )
172+ else :
173+ sys .stdout .write ("." )
174+ sys .stdout .flush ()
175+ sys .stdout .write ("\n " )
176+ sys .stdout .flush ()
177+
178+ if __name__ == "__main__" :
179+ main ()
0 commit comments