From e980f092fa37c27a110767f974bb8d24809b5bd0 Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Wed, 10 Aug 2016 00:46:38 -0700 Subject: [PATCH 1/8] in the process of refactoring. almost there. --- src/groupsplit.py | 313 ++++++++++++++++++++++++++-------------------- 1 file changed, 175 insertions(+), 138 deletions(-) diff --git a/src/groupsplit.py b/src/groupsplit.py index 29a1e48..7411b70 100644 --- a/src/groupsplit.py +++ b/src/groupsplit.py @@ -6,6 +6,7 @@ import pickle import pprint import urllib +import optparse import requests import subprocess import webbrowser @@ -14,56 +15,6 @@ from pprint import pprint from datetime import datetime -def get_client_auth(): - if os.path.isfile("consumer_oauth.json"): - with open("consumer_oauth.json", 'rb') as oauth_file: - consumer = json.load(oauth_file) - ckey = consumer['consumer_key'] - csecret = consumer['consumer_secret'] - else: - with open("consumer_oauth.json", 'wb') as oauth_file: - json.dump({'consumer_key':'YOUR KEY HERE', - 'consumer_secret':'YOUR SECRET HERE'}, oauth_file) - exit("go to https://secure.splitwise.com/oauth_clients to obtain your keys."+ - "place them in consumer_oauth.json") - return ckey, csecret - -def get_client(): - ckey, csecret = get_client_auth() - client = oauthlib.oauth1.Client(ckey, client_secret=csecret) - uri, headers, body = client.sign("https://secure.splitwise.com/api/v3.0/get_request_token", - http_method='POST') - r = requests.post(uri, headers=headers, data=body) - resp = r.text.split('&') - oauth_token = resp[0].split('=')[1] - oauth_secret = resp[1].split('=')[1] - uri = "https://secure.splitwise.com/authorize?oauth_token=%s" % oauth_token - - webbrowser.open_new(uri) - - proc = subprocess.Popen(['python', 'server.py'], stdout=subprocess.PIPE, shell=True) - stdout, stderr = proc.communicate() - if stderr: - exit(stderr) - client = oauthlib.oauth1.Client(ckey, client_secret=csecret, - resource_owner_key=oauth_token, - resource_owner_secret=oauth_secret, - verifier=stdout.strip()) - - uri, headers, body = client.sign("https://secure.splitwise.com/api/v3.0/get_access_token", - http_method='POST') - resp = requests.post(uri, headers=headers, data=body) - tokens = resp.text.split('&') - oauth_token = tokens[0].split('=')[1] - oauth_secret = tokens[1].split('=')[1] - client = oauthlib.oauth1.Client(ckey, client_secret=csecret, - resource_owner_key=oauth_token, - resource_owner_secret=oauth_secret, - verifier=stdout.strip()) - with open('oauth_client.pkl', 'wb') as pkl: - pickle.dump(client, pkl) - return client - def split(total, num_people): base = total * 100 // num_people / 100 extra = total - num_people * base @@ -71,108 +22,194 @@ def split(total, num_people): " something doesnt add up here: %d * %d + %d != %d" %(base, num_people, extra, total) return base, extra -def api_call(url, http_method, client): - uri, headers, body = client.sign(url, http_method=http_method) - resp = requests.request(http_method, uri, headers=headers, data=body) - return resp.json() - -def main(): - if os.path.isfile("oauth_client.pkl"): - with open('oauth_client.pkl', 'rb') as oauth_pkl: - client = pickle.load(oauth_pkl) - else: - client = get_client() - - resp = api_call("https://secure.splitwise.com/api/v3.0/get_current_user", 'GET', client) - my_id = resp['user']['id'] - - resp = api_call("https://secure.splitwise.com/api/v3.0/get_groups", 'GET', client) - num_found = 0 - gid = '' - members = {} - - for group in resp['groups']: - if group['name'].lower() == sys.argv[2].lower(): - gid = group['id'] - members = [m['id'] for m in group['members'] if m['id'] != my_id] - num_found += 1 - - if num_found > 1: - exit("More than 1 group found") - elif num_found < 1: - exit("No matching group") - elif len(members) < 1: - exit("No members in group") - - with open(sys.argv[1], 'rb') as csvfile: - reader = csv.reader(csvfile) - rows = [x for x in reader] - - - if os.path.isfile("csv_settings.pkl"): - with open('csv_settings.pkl', 'rb') as f: - date_col, amount_col, desc_col, has_title_row, local_currency, remember = pickle.load(f) - - else: - print "These are the first two rows of your csv" - print '\n'.join([str(t) for t in rows[0:2]]) - print 'Colnum numbers start at 0' - date_col = input("Which column has the date?") - amount_col = input("Which column has the amount?") - desc_col = input("Which column has the description?") - has_title_row = raw_input("Does first row have titles? [Y/n]").lower() != 'n' - local_currency = raw_input("What currency were these transactions made in?") - test = Money("1.00", local_currency) #pylint: disable=W0612 - remember = raw_input("Remember these settings? [Y/n]").lower() != 'n' - - if remember: - with open("csv_settings.pkl", "wb") as pkl: - csv_settings = (date_col, amount_col, desc_col, has_title_row, local_currency, remember) - pickle.dump(csv_settings, pkl) - - if has_title_row: - rows = rows[1:] - transactions = [{"date": datetime.strftime(datetime.strptime(r[date_col], "%m/%d/%y"), "%Y-%m-%dT%H:%M:%SZ"), - "amount": -1 * Money(r[amount_col], local_currency), - "desc": re.sub('\s+',' ', r[desc_col])} - for r in rows if float(r[amount_col]) < 0] - splits = [] - for t in transactions: - if raw_input("%s at %s $%s. Split? [y/N]" % (t['date'], t['desc'], t['amount'])).lower() == 'y': - splits.append(t) - +class Splitwise: + def __init__(self): + if os.path.isfile("oauth_client.pkl"): + with open('oauth_client.pkl', 'rb') as oauth_pkl: + self.client = pickle.load(oauth_pkl) + else: + self.get_client() + + def get_client_auth(self): + if os.path.isfile("consumer_oauth.json"): + with open("consumer_oauth.json", 'rb') as oauth_file: + consumer = json.load(oauth_file) + ckey = consumer['consumer_key'] + csecret = consumer['consumer_secret'] + else: + with open("consumer_oauth.json", 'wb') as oauth_file: + json.dump({'consumer_key':'YOUR KEY HERE', + 'consumer_secret':'YOUR SECRET HERE'}, oauth_file) + exit("go to https://secure.splitwise.com/oauth_clients to obtain your keys."+ + "place them in consumer_oauth.json") + self.ckey = ckey + self.csecret = csecret + + def get_client(self): + self.get_client_auth() + client = oauthlib.oauth1.Client(self.ckey, client_secret=self.csecret) + uri, headers, body = client.sign("https://secure.splitwise.com/api/v3.0/get_request_token", + http_method='POST') + r = requests.post(uri, headers=headers, data=body) + resp = r.text.split('&') + oauth_token = resp[0].split('=')[1] + oauth_secret = resp[1].split('=')[1] + uri = "https://secure.splitwise.com/authorize?oauth_token=%s" % oauth_token + + webbrowser.open_new(uri) + + proc = subprocess.Popen(['python', 'server.py'], stdout=subprocess.PIPE) + stdout, stderr = proc.communicate() + if stderr: + exit(stderr) + client = oauthlib.oauth1.Client(self.ckey, client_secret=self.csecret, + resource_owner_key=oauth_token, + resource_owner_secret=oauth_secret, + verifier=stdout.strip()) + + uri, headers, body = client.sign("https://secure.splitwise.com/api/v3.0/get_access_token", + http_method='POST') + resp = requests.post(uri, headers=headers, data=body) + tokens = resp.text.split('&') + oauth_token = tokens[0].split('=')[1] + oauth_secret = tokens[1].split('=')[1] + client = oauthlib.oauth1.Client(self.ckey, client_secret=self.csecret, + resource_owner_key=oauth_token, + resource_owner_secret=oauth_secret, + verifier=stdout.strip()) + with open('oauth_client.pkl', 'wb') as pkl: + pickle.dump(client, pkl) + self.client = client + + def api_call(self, url, http_method): + uri, headers, body = self.client.sign(url, http_method=http_method) + resp = requests.request(http_method, uri, headers=headers, data=body) + return resp.json() + + def get_id(self): + if not hasattr(self, "my_id"): + resp = self.api_call("https://secure.splitwise.com/api/v3.0/get_current_user", 'GET') + self.my_id = resp['user']['id'] + return self.my_id + + def get_groups(self): + resp = self.api_call("https://secure.splitwise.com/api/v3.0/get_groups", 'GET') + return resp['groups'] + + def post_expense(self, uri): + resp = self.api_call(uri, 'POST') + if resp["errors"]: + print "URI:" + print uri + pprint(resp) + else: + sys.stdout.write(".") + sys.stdout.flush() - print "Uploading %d splits" % len(splits) - one_cent = Money("0.01", local_currency) - for s in splits: - num_people = len(members) + 1 +class SplitGenerator(): + def __init__(self, options, args, api): + self.api = api + csv_file = args[0] + group_name = args[1] + with open(csv_file, 'rb') as csvfile: + reader = csv.reader(csvfile) + self.rows = [x for x in reader] + + + if os.path.isfile("csv_settings.pkl"): + with open('csv_settings.pkl', 'rb') as f: + self = pickle.load(f) + + else: + print "These are the first two rows of your csv" + print '\n'.join([str(t) for t in self.rows[0:2]]) + print 'Colnum numbers start at 0' + self.date_col = input("Which column has the date?") + self.amount_col = input("Which column has the amount?") + self.desc_col = input("Which column has the description?") + self.has_title_row = raw_input("Does first row have titles? [Y/n]").lower() != 'n' + self.local_currency = raw_input("What currency were these transactions made in?").upper() + self.test = Money("1.00", self.local_currency) #pylint: disable=W0612 + self.remember = raw_input("Remember these settings? [Y/n]").lower() != 'n' + + if self.remember: + with open("csv_settings.pkl", "wb") as pkl: + pickle.dump(self, pkl) + + if self.has_title_row: + self.rows = self.rows[1:] + + self.make_transactions() + self.get_group(group_name) + self.splits = [] + self.ask_for_splits() + + def make_transactions(self): + self.transactions = [{"date": datetime.strftime(datetime.strptime(r[self.date_col], "%m/%d/%y"), "%Y-%m-%dT%H:%M:%SZ"), + "amount": -1 * Money(r[self.amount_col], self.local_currency), + "desc": re.sub('\s+',' ', r[self.desc_col])} + for r in self.rows if float(r[self.amount_col]) < 0] + + def get_group(self, name): + num_found = 0 + gid = '' + members = {} + groups = self.api.get_groups() + for group in groups: + if group['name'].lower() == name.lower(): + gid = group['id'] + members = [m['id'] for m in group['members'] if m['id'] != self.api.get_id()] + num_found += 1 + + if num_found > 1: + exit("More than 1 group found") + elif num_found < 1: + exit("No matching group") + elif len(members) < 1: + exit("No members in group") + + self.members = members + self.gid = gid + + def ask_for_splits(self): + for t in self.transactions: + if raw_input("%s at %s $%s. Split? [y/N]" % (t['date'], t['desc'], t['amount'])).lower() == 'y': + self.splits.append(t) + + def __getitem__(self, index): + s = self.splits[index] + one_cent = Money("0.01", self.local_currency) + num_people = len(self.members) + 1 base, extra = split(s['amount'], num_people) params = { "payment": 'false', "cost": s["amount"].amount, "description": s["desc"], "date": s["date"], - "group_id": gid, - "currency_code": local_currency, - "users__0__user_id": my_id, + "group_id": self.gid, + "currency_code": self.local_currency, + "users__0__user_id": self.api.get_id(), "users__0__paid_share": s["amount"].amount, "users__0__owed_share": base.amount, } - for i in range(len(members)): - params['users__%s__user_id' % (i+1)] = members[i] + for i in range(len(self.members)): + params['users__%s__user_id' % (i+1)] = sef.members[i] params['users__%s__paid_share' % (i+1)] = 0 params['users__%s__owed_share' % (i+1)] = (base + one_cent).amount if extra else base.amount extra -= one_cent paramsStr = urllib.urlencode(params) - uri = "https://secure.splitwise.com/api/v3.0/create_expense?%s" % (paramsStr) - resp = api_call(uri, 'POST', client) - if resp["errors"]: - print "URI:" - print uri - pprint(resp) - else: - sys.stdout.write(".") - sys.stdout.flush() + return "https://secure.splitwise.com/api/v3.0/create_expense?%s" % (paramsStr) + + +def main(): + parser = optparse.OptionParser() + parser.add_option('-v', '--verbose', default=False, dest='verbose') + options, args = parser.parse_args() + splitwise = Splitwise() + split_gen = SplitGenerator(options, args, splitwise) + print "Uploading splits" + for uri in split_gen: + splitwise.post_expense(uri) sys.stdout.write("\n") sys.stdout.flush() From 0dc5c0f8abcb6cf68145a191677b01193fbcab26 Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Fri, 12 Aug 2016 10:42:17 -0700 Subject: [PATCH 2/8] added test client for splitwise api --- .travis.yml | 11 ++-- src/groupsplit.py | 120 +++++++++++++++++++++++++------------- test/.gitignore | 4 ++ test/oauth_client.pkl.enc | Bin 0 -> 640 bytes test/testSplit.py | 18 +++++- test/transactions.csv | 6 ++ 6 files changed, 114 insertions(+), 45 deletions(-) create mode 100644 test/.gitignore create mode 100644 test/oauth_client.pkl.enc create mode 100644 test/transactions.csv diff --git a/.travis.yml b/.travis.yml index d070dbf..a52dc57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: python python: - - "2.7" -# command to install dependencies -install: "pip install -r setup/requirements.txt" -# command to run tests -script: "cd test; nosetests" +- '2.7' +install: pip install -r setup/requirements.txt +script: cd test; nosetests +before_install: +- openssl aes-256-cbc -K $encrypted_2931867a4314_key -iv $encrypted_2931867a4314_iv + -in oauth_client.pkl.enc -out oauth_client.pkl -d diff --git a/src/groupsplit.py b/src/groupsplit.py index 7411b70..6b22deb 100644 --- a/src/groupsplit.py +++ b/src/groupsplit.py @@ -6,6 +6,7 @@ import pickle import pprint import urllib +import logging import optparse import requests import subprocess @@ -15,6 +16,28 @@ from pprint import pprint from datetime import datetime +LOGGING_DISABELED = 100 +log_levels = [LOGGING_DISABELED, logging.CRITICAL, logging.ERROR, + logging.WARNING, logging.INFO, logging.DEBUG] +# Adapted from: +# https://docs.python.org/2/howto/logging.html#configuring-logging +# create logger +logger = logging.getLogger(__name__) + +# create console handler and set level to debug +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) + +# create formatter +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + +# add formatter to ch +ch.setFormatter(formatter) + +# add ch to logger +logger.addHandler(ch) +logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') + def split(total, num_people): base = total * 100 // num_people / 100 extra = total - num_people * base @@ -23,9 +46,9 @@ def split(total, num_people): return base, extra class Splitwise: - def __init__(self): - if os.path.isfile("oauth_client.pkl"): - with open('oauth_client.pkl', 'rb') as oauth_pkl: + def __init__(self, options, args): + if os.path.isfile(options.api_client): + with open(options.api_client, 'rb') as oauth_pkl: self.client = pickle.load(oauth_pkl) else: self.get_client() @@ -99,44 +122,55 @@ def get_groups(self): def post_expense(self, uri): resp = self.api_call(uri, 'POST') if resp["errors"]: - print "URI:" - print uri - pprint(resp) + sys.stderr.write( "URI:") + sys.stderr.write(uri) + pprint(resp, stream=sys.stderr) else: sys.stdout.write(".") sys.stdout.flush() +class CsvSettings(): + def __init__(self, rows): + print "These are the first two rows of your csv" + print '\n'.join([str(t) for t in rows[0:2]]) + print 'Colnum numbers start at 0' + self.date_col = input("Which column has the date?") + self.amount_col = input("Which column has the amount?") + self.desc_col = input("Which column has the description?") + self.has_title_row = raw_input("Does first row have titles? [Y/n]").lower() != 'n' + while True: + try: + self.local_currency = raw_input("What currency were these transactions made in?").upper() + test = Money("1.00", self.local_currency) #pylint: disable=W0612 + except ValueError as err: + print err + print "Try again..." + else: + break + self.remember = raw_input("Remember these settings? [Y/n]").lower() != 'n' + + if self.remember: + with open("csv_settings.pkl", "wb") as pkl: + pickle.dump(self, pkl) + class SplitGenerator(): def __init__(self, options, args, api): - self.api = api csv_file = args[0] group_name = args[1] + self.api = api + self.options = options + self.args = args with open(csv_file, 'rb') as csvfile: reader = csv.reader(csvfile) self.rows = [x for x in reader] - - - if os.path.isfile("csv_settings.pkl"): - with open('csv_settings.pkl', 'rb') as f: - self = pickle.load(f) - + + if os.path.isfile(options.csv_settings): + with open(options.csv_settings, 'rb') as f: + self.csv = pickle.load(f) else: - print "These are the first two rows of your csv" - print '\n'.join([str(t) for t in self.rows[0:2]]) - print 'Colnum numbers start at 0' - self.date_col = input("Which column has the date?") - self.amount_col = input("Which column has the amount?") - self.desc_col = input("Which column has the description?") - self.has_title_row = raw_input("Does first row have titles? [Y/n]").lower() != 'n' - self.local_currency = raw_input("What currency were these transactions made in?").upper() - self.test = Money("1.00", self.local_currency) #pylint: disable=W0612 - self.remember = raw_input("Remember these settings? [Y/n]").lower() != 'n' + self.csv = CsvSettings(self.rows) - if self.remember: - with open("csv_settings.pkl", "wb") as pkl: - pickle.dump(self, pkl) - - if self.has_title_row: + if self.csv.has_title_row: self.rows = self.rows[1:] self.make_transactions() @@ -145,10 +179,10 @@ def __init__(self, options, args, api): self.ask_for_splits() def make_transactions(self): - self.transactions = [{"date": datetime.strftime(datetime.strptime(r[self.date_col], "%m/%d/%y"), "%Y-%m-%dT%H:%M:%SZ"), - "amount": -1 * Money(r[self.amount_col], self.local_currency), - "desc": re.sub('\s+',' ', r[self.desc_col])} - for r in self.rows if float(r[self.amount_col]) < 0] + self.transactions = [{"date": datetime.strftime(datetime.strptime(r[self.csv.date_col], "%m/%d/%y"), "%Y-%m-%dT%H:%M:%SZ"), + "amount": -1 * Money(r[self.csv.amount_col], self.csv.local_currency), + "desc": re.sub('\s+',' ', r[self.csv.desc_col])} + for r in self.rows if float(r[self.csv.amount_col]) < 0] def get_group(self, name): num_found = 0 @@ -173,12 +207,12 @@ def get_group(self, name): def ask_for_splits(self): for t in self.transactions: - if raw_input("%s at %s $%s. Split? [y/N]" % (t['date'], t['desc'], t['amount'])).lower() == 'y': + if self.options.yes or raw_input("%s at %s $%s. Split? [y/N]" % (t['date'], t['desc'], t['amount'])).lower() == 'y': self.splits.append(t) def __getitem__(self, index): s = self.splits[index] - one_cent = Money("0.01", self.local_currency) + one_cent = Money("0.01", self.csv.local_currency) num_people = len(self.members) + 1 base, extra = split(s['amount'], num_people) params = { @@ -187,15 +221,15 @@ def __getitem__(self, index): "description": s["desc"], "date": s["date"], "group_id": self.gid, - "currency_code": self.local_currency, + "currency_code": self.csv.local_currency, "users__0__user_id": self.api.get_id(), "users__0__paid_share": s["amount"].amount, "users__0__owed_share": base.amount, } for i in range(len(self.members)): - params['users__%s__user_id' % (i+1)] = sef.members[i] + params['users__%s__user_id' % (i+1)] = self.members[i] params['users__%s__paid_share' % (i+1)] = 0 - params['users__%s__owed_share' % (i+1)] = (base + one_cent).amount if extra else base.amount + params['users__%s__owed_share' % (i+1)] = (base + one_cent).amount if extra.amount > 0 else base.amount extra -= one_cent paramsStr = urllib.urlencode(params) return "https://secure.splitwise.com/api/v3.0/create_expense?%s" % (paramsStr) @@ -203,12 +237,20 @@ def __getitem__(self, index): def main(): parser = optparse.OptionParser() - parser.add_option('-v', '--verbose', default=False, dest='verbose') + parser.add_option('-v', '--verbosity', default=2, dest='verbosity', help='change the logging level (0 - 6) default: 2') + parser.add_option('-y','',default=False, action='store_true', dest='yes', help='split all transactions in csv without confirmation') + parser.add_option('-d', '--dryrun', default=False, action='store_true', dest='dryrun', help='prints requests instead of sending them') + parser.add_option('', '--csv-settings', default='csv_settings.pkl', dest='csv_settings', help='supply different csv_settings object (for testing mostly)') + parser.add_option('', '--api-client', default='oauth_client.pkl', dest='api_client', help='supply different splitwise api client (for testing mostly)') options, args = parser.parse_args() - splitwise = Splitwise() + logger.setLevel(log_levels[options.verbosity]) + splitwise = Splitwise(options, args) split_gen = SplitGenerator(options, args, splitwise) print "Uploading splits" for uri in split_gen: + if options.dryrun: + print uri + continue splitwise.post_expense(uri) sys.stdout.write("\n") sys.stdout.flush() diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..172a6d3 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,4 @@ +*~ +*.pyc +consumer_oauth.json +oauth_client.pkl \ No newline at end of file diff --git a/test/oauth_client.pkl.enc b/test/oauth_client.pkl.enc new file mode 100644 index 0000000000000000000000000000000000000000..eaf098e2a5bb31802f5121cac34031240450a58c GIT binary patch literal 640 zcmV-`0)PG2@{Z&MB;HUv*#$Qo7wu+s^B*$9% zz8c|_Of>;Ep$_5C+UhsQ@i*O0(RNRRXw3lBM>6|YpT(%RKFMSfCz1REl*1xX|CN0Z z7+g$@&a->?R@}FtSNBt&`Q4H0+5)m44?<6XO8p74>W}2!OBrz&*$v%Or7ZYsb(6; zh{pJ_3`cRL1UQEE-R?h`Y9{$#T3?FAO`eEs_N_X;W&%EqtK58puV4@IA*W+A1{J$` z96RO2?yMtqw(kF)g$~mwHC!F!tn-Nz#`oXM{V#)(xO#hN7={@`cWl5R1AVw;3Q!xM a4yC|Q!y3kmt(Hg}z<|3i5I literal 0 HcmV?d00001 diff --git a/test/testSplit.py b/test/testSplit.py index d354865..d9dec05 100644 --- a/test/testSplit.py +++ b/test/testSplit.py @@ -1,5 +1,6 @@ import unittest import sys +import subprocess from money import Money sys.path.append("../src") from groupsplit import split @@ -10,8 +11,23 @@ def test_splits(self): {"amount": "1.00", "ppl": 2, "expect": ("0.50","0.00")}, {"amount": "1.00", "ppl": 3, "expect": ("0.33", "0.01")}, {"amount": "12.97", "ppl": 5, "expect": ("2.59", "0.02")}, - {"amount": "52000", "ppl": 3, "expect": ("17333.33", "0.01")}, + {"amount": "52000.00", "ppl": 3, "expect": ("17333.33", "0.01")}, + {"amount": "1050.00", "ppl": 3, "expect": ("350.00", "0.00")}, ] for case in cases: expect = (Money(case['expect'][0], "CAD"), Money(case['expect'][1], "CAD")) self.assertEqual(expect, split(Money(case['amount'], "CAD"), case['ppl'])) + +class SystemTests(unittest.TestCase): + def test_group_of_2(self): + proc = subprocess.Popen(['python', '../src/groupsplit.py', 'transactions.csv', 'group_of_2', + '--csv-settings=csv_settings.pkl', '--api-client=oauth_client.pkl', + '-y'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = proc.communicate() + self.assertEqual(stderr, '') + def test_group_of_3(self): + proc = subprocess.Popen(['python', '../src/groupsplit.py', 'transactions.csv', 'group_of_3', + '--csv-settings=csv_settings.pkl', '--api-client=oauth_client.pkl', + '-y'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = proc.communicate() + self.assertEqual(stderr, '') diff --git a/test/transactions.csv b/test/transactions.csv new file mode 100644 index 0000000..7539889 --- /dev/null +++ b/test/transactions.csv @@ -0,0 +1,6 @@ +7/5/16,-1.56,-,"POS Purchase","Test1" +7/5/16,134.12,-,"Deposit","Deposit should be ignored" +7/4/16,-1050.00,-,"POS Purchase","Big purchase" +7/2/16,-5.45,-,"POS Purchase","Test2" +7/2/16,-10.98,-,"POS Purchase","Test3" +6/30/16,-46.31,-,"POS Purchase","Test4" From ab20ba3e7a9fd111070886d34edb6bcbf89c4ef4 Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Fri, 12 Aug 2016 10:45:17 -0700 Subject: [PATCH 3/8] fixed travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a52dc57..481efd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,4 @@ install: pip install -r setup/requirements.txt script: cd test; nosetests before_install: - openssl aes-256-cbc -K $encrypted_2931867a4314_key -iv $encrypted_2931867a4314_iv - -in oauth_client.pkl.enc -out oauth_client.pkl -d + -in test/oauth_client.pkl.enc -out test/oauth_client.pkl -d From 99eb0b6c56eb46164e2d81d5b0bbdb50809fe882 Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Fri, 12 Aug 2016 10:55:27 -0700 Subject: [PATCH 4/8] adding csv_settings to test folder --- .gitignore | 3 +-- src/.gitignore | 5 +++++ test/csv_settings.pkl | 24 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 src/.gitignore create mode 100644 test/csv_settings.pkl diff --git a/.gitignore b/.gitignore index 3b59d38..04f5b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ *~ +*# *.pyc -consumer_oauth.json -*.pkl *bank* \ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..3b59d38 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,5 @@ +*~ +*.pyc +consumer_oauth.json +*.pkl +*bank* \ No newline at end of file diff --git a/test/csv_settings.pkl b/test/csv_settings.pkl new file mode 100644 index 0000000..0e2e79b --- /dev/null +++ b/test/csv_settings.pkl @@ -0,0 +1,24 @@ +(i__main__ +CsvSettings +p0 +(dp1 +S'remember' +p2 +I01 +sS'has_title_row' +p3 +I01 +sS'amount_col' +p4 +I1 +sS'desc_col' +p5 +I4 +sS'local_currency' +p6 +S'CAD' +p7 +sS'date_col' +p8 +I0 +sb. \ No newline at end of file From a4f08849a97dc321465772a36dbcff06811d10a4 Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Fri, 12 Aug 2016 11:44:38 -0700 Subject: [PATCH 5/8] adding more tests --- src/groupsplit.py | 15 +++++++++++---- test/testSplit.py | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/groupsplit.py b/src/groupsplit.py index 6b22deb..32403f4 100644 --- a/src/groupsplit.py +++ b/src/groupsplit.py @@ -46,9 +46,9 @@ def split(total, num_people): return base, extra class Splitwise: - def __init__(self, options, args): - if os.path.isfile(options.api_client): - with open(options.api_client, 'rb') as oauth_pkl: + def __init__(self, api_client='oauth_client.pkl'): + if os.path.isfile(api_client): + with open(api_client, 'rb') as oauth_pkl: self.client = pickle.load(oauth_pkl) else: self.get_client() @@ -129,6 +129,13 @@ def post_expense(self, uri): sys.stdout.write(".") sys.stdout.flush() + def delete_expense(self, expense_id): + return self.api_call("https://secure.splitwise.com/api/v3.0/delete_expense/%s" % expense_id, 'POST') + + def get_expenses(self, limit=0): + resp = self.api_call("https://secure.splitwise.com/api/v3.0/get_expenses?limit=%s" % limit, 'GET') + return resp['expenses'] + class CsvSettings(): def __init__(self, rows): print "These are the first two rows of your csv" @@ -244,7 +251,7 @@ def main(): parser.add_option('', '--api-client', default='oauth_client.pkl', dest='api_client', help='supply different splitwise api client (for testing mostly)') options, args = parser.parse_args() logger.setLevel(log_levels[options.verbosity]) - splitwise = Splitwise(options, args) + splitwise = Splitwise(options.api_client) split_gen = SplitGenerator(options, args, splitwise) print "Uploading splits" for uri in split_gen: diff --git a/test/testSplit.py b/test/testSplit.py index d9dec05..87ebf04 100644 --- a/test/testSplit.py +++ b/test/testSplit.py @@ -3,9 +3,13 @@ import subprocess from money import Money sys.path.append("../src") -from groupsplit import split +from groupsplit import split, Splitwise class UtilsTests(unittest.TestCase): + def __init__(self, *args, **kwargs): + super(UtilsTests, self).__init__(*args, **kwargs) + self.api = Splitwise() + def test_splits(self): cases = [ {"amount": "1.00", "ppl": 2, "expect": ("0.50","0.00")}, @@ -18,13 +22,25 @@ def test_splits(self): expect = (Money(case['expect'][0], "CAD"), Money(case['expect'][1], "CAD")) self.assertEqual(expect, split(Money(case['amount'], "CAD"), case['ppl'])) + def test_get_id(self): + assertGreater(int(self.api.get_id()), 0) + + def test_get_groups(self): + self.assertGreater(len(self.api.get_groups()), 0) + class SystemTests(unittest.TestCase): + def __del__(self): + api = Splitwise() + for expense in api.get_expenses(): + api.delete_expense(expense['id']) + def test_group_of_2(self): proc = subprocess.Popen(['python', '../src/groupsplit.py', 'transactions.csv', 'group_of_2', '--csv-settings=csv_settings.pkl', '--api-client=oauth_client.pkl', '-y'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() self.assertEqual(stderr, '') + def test_group_of_3(self): proc = subprocess.Popen(['python', '../src/groupsplit.py', 'transactions.csv', 'group_of_3', '--csv-settings=csv_settings.pkl', '--api-client=oauth_client.pkl', From bafc4aa91356ce72472af066a02f3e864dbaf01b Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Fri, 12 Aug 2016 11:47:48 -0700 Subject: [PATCH 6/8] fixed typo --- test/testSplit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testSplit.py b/test/testSplit.py index 87ebf04..7ac3827 100644 --- a/test/testSplit.py +++ b/test/testSplit.py @@ -23,7 +23,7 @@ def test_splits(self): self.assertEqual(expect, split(Money(case['amount'], "CAD"), case['ppl'])) def test_get_id(self): - assertGreater(int(self.api.get_id()), 0) + self.assertGreater(int(self.api.get_id()), 0) def test_get_groups(self): self.assertGreater(len(self.api.get_groups()), 0) From c37140689e08881544c11a1a1f1b9759013f3fe7 Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Fri, 12 Aug 2016 11:55:31 -0700 Subject: [PATCH 7/8] Update transactions.csv --- test/transactions.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/test/transactions.csv b/test/transactions.csv index 7539889..5f28bb6 100644 --- a/test/transactions.csv +++ b/test/transactions.csv @@ -4,3 +4,4 @@ 7/2/16,-5.45,-,"POS Purchase","Test2" 7/2/16,-10.98,-,"POS Purchase","Test3" 6/30/16,-46.31,-,"POS Purchase","Test4" +6/30/16,-0.02,-,"POS Purchase","Small purchase" From f0e16cc0f1dc9f2a4702be6b0c1d3af93580f1a1 Mon Sep 17 00:00:00 2001 From: Eric Secules Date: Fri, 12 Aug 2016 11:57:18 -0700 Subject: [PATCH 8/8] added small amounts to split function test. --- test/testSplit.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/testSplit.py b/test/testSplit.py index 7ac3827..c61fd0a 100644 --- a/test/testSplit.py +++ b/test/testSplit.py @@ -17,6 +17,8 @@ def test_splits(self): {"amount": "12.97", "ppl": 5, "expect": ("2.59", "0.02")}, {"amount": "52000.00", "ppl": 3, "expect": ("17333.33", "0.01")}, {"amount": "1050.00", "ppl": 3, "expect": ("350.00", "0.00")}, + {"amount": "0.02", "ppl": 3, "expect": ("0.00", "0.02")}, + {"amount": "0.02", "ppl": 2, "expect": ("0.01", "0.00")}, ] for case in cases: expect = (Money(case['expect'][0], "CAD"), Money(case['expect'][1], "CAD"))