def test_roundtrip(self): args = parser.parse_known_args()[0] # 1. gets sync data from server # 2. tests that to_api(from_api(data)) is the same thing client = clientfromargs(args, sync=False) sync_data = client.catalogClient.get_sync_data_obj() budget_version_id = next(d['id'] for d in sync_data['changed_entities']['ce_budget_versions'] if d['version_name'] == args.budgetname) client.budget_version_id = budget_version_id for objclient in (client.budgetClient, client.catalogClient): sync_data = objclient.get_sync_data_obj() server_changed_entities = sync_data['changed_entities'] for key in server_changed_entities: if key in objclient.obj.listfields: if len(server_changed_entities[key]) == 0: continue obj_dict = server_changed_entities[key][0] typ = objclient.obj.listfields[key] obj_dict2 = typ.from_apidict(obj_dict).get_apidict() diff = DictDiffer(obj_dict2, obj_dict) for k in diff.changed(): AssertionError('changed {}: {}->{}'.format(k, obj_dict[k], obj_dict2[k])) for k in diff.removed(): AssertionError('removed {}: {}'.format(k, obj_dict[k])) for k in diff.added(): AssertionError('added {}: {}'.format(k, obj_dict2[k])) elif key in objclient.obj.scalarfields: obj_dict2 = objclient.obj.from_apidict(server_changed_entities).get_apidict() if server_changed_entities[key] != obj_dict2[key]: AssertionError('changed {}: {}->{}'.format(key, server_changed_entities[key], obj_dict2[key]))
def ofximport(cls): """Manually import an OFX into a nYNAB budget""" print('pynYNAB OFX import') args = cls.ofximport_parser.parse_args() verify_common_args(args) client = clientfromargs(args) delta = do_ofximport(args, client) client.push(expected_delta=delta)
def csvimport(cls): """Manually import a CSV into a nYNAB budget""" print('pynYNAB CSV import') args = cls.csvimport_parser.parse_args() verify_common_args(args) schema = verify_csvimport(args) client = clientfromargs(args) delta = do_csvimport(args, schema, client) client.push(expected_delta=delta)
def csvimport(cls): """Manually import a CSV into a nYNAB budget""" print('pynYNAB CSV import') args = cls.csvimport_parser.parse_args() verify_common_args(args) if not os.path.exists(args.csvfile): LOG.error('input CSV file does not exist') exit(-1) client = clientfromargs(args) delta = do_csvimport(args, client) client.push(expected_delta=delta)
def reload(self): # parser = configargparse.getArgumentParser('pynYNAB') args = parser.parse_known_args()[0] self.client = clientfromargs(args)
def setUp(self): args = parser.parse_known_args()[0] args.budgetname = test_budget_name self.client = clientfromargs(args, sync=False) self.client.catalogClient.sync() self.client.select_budget(test_budget_name)
def do_ofximport(args, stmts, client=None): delta = 0 if client is None: client = clientfromargs(args) accounts = client.budget.be_accounts accountvsnotes = { account.note: account for account in accounts if account.note is not None } for stmt in stmts: key = stmt.account.bankid + ' ' + stmt.account.branchid + ' ' + stmt.account.acctid if all(key not in note for note in accountvsnotes): if len(accounts) == 0: print('No accounts available in this budget') exit(-1) # ask user input for which bank account this is, then save it into the account note in nYNAB account = client.select_account_ui() # Save the selection in the nYNAB account note addon = 'key[' + key + ']key' if account.note is not None: account.note += addon else: account.note = addon else: for note in accountvsnotes: if key in note: account = accountvsnotes[note] imported_date = datetime.now().date() for ofx_transaction in stmt.transactions: payee_name = ofx_transaction.name if ofx_transaction.payee is None else ofx_transaction.payee try: payee = next(p for p in client.budget.be_payees if p.name == payee_name) except StopIteration: payee = Payee(name=payee_name) client.budget.be_payees.append(payee) delta += 1 # use ftid so we don't import duplicates if not any( ofx_transaction.fitid in transaction.memo for transaction in client.budget.be_transactions if transaction.memo is not None and transaction.entities_account_id == account.id): transaction = Transaction( date=ofx_transaction.dtposted, memo=ofx_transaction.memo + ' ' + ofx_transaction.fitid, imported_payee=payee_name, entities_payee_id=payee.id, imported_date=imported_date, source="Imported", check_number=ofx_transaction.checknum, amount=float(ofx_transaction.trnamt), entities_account_id=account.id) client.budget.be_transactions.append(transaction) delta += 1 return delta
def do_csvimport(args, client=None): delta = 0 if client is None: client = clientfromargs(args) LOG.debug('selected schema %s' % (args.schema,)) if os.path.exists(args.schema): schemafile = args.schema else: schemafile = os.path.join(schemas_dir, args.schema + '.json') if not os.path.exists(schemafile): LOG.error('This schema doesn''t exist in csv_schemas') exit(-1) try: schema = SchemaModel(schemafile, case_insensitive_headers=True) with open(schemafile, 'r') as sf: schemacontent = json.load(sf) try: nheaders = schemacontent['nheaders'] except KeyError: nheaders = 1 except InvalidSchemaError: LOG.error('Invalid CSV schema') raise LOG.debug('schema headers %s' % schema.headers) if 'account' not in schema.headers and args.accountname is None: LOG.error('This schema does not have an account column and no account name was provided') exit(-1) accounts = {x.account_name: x for x in client.budget.be_accounts} payees = {p.name: p for p in client.budget.be_payees} mastercategories_perid = {m.id: m for m in client.budget.be_master_categories} subcategories = {} for s in client.budget.be_subcategories: m = mastercategories_perid[s.entities_master_category_id] subcategories[m.name + ':' + s.name] = s def getaccount(accountname): try: LOG.debug('searching for account %s' % accountname) return accounts[accountname] except KeyError: LOG.error('Couldn''t find this account: %s' % accountname) exit(-1) def getpayee(payeename): try: LOG.debug('searching for payee %s' % payeename) return payees[payeename] except KeyError: LOG.debug('Couldn''t find this payee: %s' % payeename) payee = Payee(name=payeename) client.budget.be_payees.append(payee) delta += 1 return payee def getsubcategory(categoryname): try: LOG.debug('searching for subcategory %s' % categoryname) return subcategories[categoryname] except KeyError: LOG.debug('Couldn''t find this category: %s' % categoryname) exit(-1) entities_account_id = None if 'account' not in schema.headers: entities_account_id = getaccount(args.accountname).id amount = None if 'inflow' in schema.headers and 'outflow' in schema.headers: pass elif 'amount' in schema.headers: pass else: LOG.error('This schema doesn''t provide an amount column or (inflow,outflow) columns') exit(-1) csvrow = namedtuple('CSVrow', field_names=schema.headers) imported_date = datetime.now().date() LOG.debug('OK starting the import from %s ' % os.path.abspath(args.csvfile)) with open(args.csvfile, 'r') as inputfile: header = [] for i in range(0, nheaders): header.append(inputfile.readline()) for row in csv.reader(inputfile): if sys.version[0] == '2': row = [cell.decode('utf-8') for cell in row] if all(map(lambda x: x.strip() == '', row)): continue LOG.debug('read line %s' % row) result = csvrow(*list(schema.convert_row(*row, fail_fast=True))) if 'account' in schema.headers: entities_account_id = getaccount(result.account).id if entities_account_id is None: LOG.error( 'No account id, the account %s in the an account column was not recognized' % result.account) exit(-1) if 'inflow' in schema.headers and 'outflow' in schema.headers: amount = result.inflow - result.outflow elif 'amount' in schema.headers: amount = result.amount if 'category' in schema.headers and result.category: entities_subcategory_id = getsubcategory(result.category).id else: entities_subcategory_id = None if 'payee' in schema.headers: imported_payee = result.payee else: imported_payee = '' entities_payee_id = getpayee(imported_payee).id if 'memo' in schema.headers: memo = result.memo else: memo = '' transaction = Transaction( entities_account_id=entities_account_id, cash_amount=amount, amount=amount, date=result.date, entities_payee_id=entities_payee_id, entities_subcategory_id=entities_subcategory_id, imported_date=imported_date, imported_payee=imported_payee, memo=memo, source="Imported" ) if args.import_duplicates or not (transaction.key2 in [tr.key2 for tr in client.budget.be_transactions]): LOG.debug('Appending transaction %s ' % transaction.get_dict()) client.budget.be_transactions.append(transaction) delta += 1 else: LOG.debug('Duplicate transaction found %s ' % transaction.get_dict()) return delta
import random from datetime import datetime from pynYNAB.ClientFactory import clientfromargs from pynYNAB.__main__ import parser from pynYNAB.schema.budget import Transaction # used to ping the nYNAB API to check that the sync works args = parser.parse_known_args()[0] N = 20 client = clientfromargs(args) client.sync() account = next(acc for acc in client.budget.be_accounts) for _ in range(0, N): transaction = Transaction(cleared='Uncleared', date=datetime.now(), entities_account=account, amount=random.randrange(-10, 10)) client.add_transaction(transaction)