Skip to content

Commit 0dc5c0f

Browse files
committed
added test client for splitwise api
1 parent e980f09 commit 0dc5c0f

6 files changed

Lines changed: 114 additions & 45 deletions

File tree

.travis.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
language: python
22
python:
3-
- "2.7"
4-
# command to install dependencies
5-
install: "pip install -r setup/requirements.txt"
6-
# command to run tests
7-
script: "cd test; nosetests"
3+
- '2.7'
4+
install: pip install -r setup/requirements.txt
5+
script: cd test; nosetests
6+
before_install:
7+
- openssl aes-256-cbc -K $encrypted_2931867a4314_key -iv $encrypted_2931867a4314_iv
8+
-in oauth_client.pkl.enc -out oauth_client.pkl -d

src/groupsplit.py

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pickle
77
import pprint
88
import urllib
9+
import logging
910
import optparse
1011
import requests
1112
import subprocess
@@ -15,6 +16,28 @@
1516
from pprint import pprint
1617
from 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+
1841
def 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

2548
class 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+
109156
class 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

204238
def 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()

test/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*~
2+
*.pyc
3+
consumer_oauth.json
4+
oauth_client.pkl

test/oauth_client.pkl.enc

640 Bytes
Binary file not shown.

test/testSplit.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import unittest
22
import sys
3+
import subprocess
34
from money import Money
45
sys.path.append("../src")
56
from groupsplit import split
@@ -10,8 +11,23 @@ def test_splits(self):
1011
{"amount": "1.00", "ppl": 2, "expect": ("0.50","0.00")},
1112
{"amount": "1.00", "ppl": 3, "expect": ("0.33", "0.01")},
1213
{"amount": "12.97", "ppl": 5, "expect": ("2.59", "0.02")},
13-
{"amount": "52000", "ppl": 3, "expect": ("17333.33", "0.01")},
14+
{"amount": "52000.00", "ppl": 3, "expect": ("17333.33", "0.01")},
15+
{"amount": "1050.00", "ppl": 3, "expect": ("350.00", "0.00")},
1416
]
1517
for case in cases:
1618
expect = (Money(case['expect'][0], "CAD"), Money(case['expect'][1], "CAD"))
1719
self.assertEqual(expect, split(Money(case['amount'], "CAD"), case['ppl']))
20+
21+
class SystemTests(unittest.TestCase):
22+
def test_group_of_2(self):
23+
proc = subprocess.Popen(['python', '../src/groupsplit.py', 'transactions.csv', 'group_of_2',
24+
'--csv-settings=csv_settings.pkl', '--api-client=oauth_client.pkl',
25+
'-y'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
26+
stdout, stderr = proc.communicate()
27+
self.assertEqual(stderr, '')
28+
def test_group_of_3(self):
29+
proc = subprocess.Popen(['python', '../src/groupsplit.py', 'transactions.csv', 'group_of_3',
30+
'--csv-settings=csv_settings.pkl', '--api-client=oauth_client.pkl',
31+
'-y'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
32+
stdout, stderr = proc.communicate()
33+
self.assertEqual(stderr, '')

test/transactions.csv

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
7/5/16,-1.56,-,"POS Purchase","Test1"
2+
7/5/16,134.12,-,"Deposit","Deposit should be ignored"
3+
7/4/16,-1050.00,-,"POS Purchase","Big purchase"
4+
7/2/16,-5.45,-,"POS Purchase","Test2"
5+
7/2/16,-10.98,-,"POS Purchase","Test3"
6+
6/30/16,-46.31,-,"POS Purchase","Test4"

0 commit comments

Comments
 (0)