예제 #1
0
    def __init__(self, **kwargs):
        self.server_entities = {}
        self.budget_version_id = None
        self.logger = kwargs.get('logger', None)
        if self.logger is None:
            self.logger = get_logger()

        self.budget_name = kwargs.get('budgetname', None)
        if self.budget_name is None:
            logger.error('No budget name was provided')
            exit(-1)
        self.connection = kwargs.get('nynabconnection', None)
        self.catalog = Catalog()
        self.budget = Budget()
        self.budget_version = BudgetVersion()

        self.current_device_knowledge = {}
        self.device_knowledge_of_server = {}
        self.starting_device_knowledge = 0
        self.ending_device_knowledge = 0

        engine = kwargs.get('engine', create_engine('sqlite://'))

        Base.metadata.create_all(engine)
        self.Session = sessionmaker(bind=engine)

        self.session = self.Session()
        self.session.add(self.catalog)
        self.session.add(self.budget)
        self.session.commit()

        self.online = self.connection is not None
        self.catalogClient = RootObjClient(self.catalog, self, 'syncCatalogData')
        self.budgetClient = RootObjClient(self.budget, self, 'syncBudgetData')
예제 #2
0
 def setUp(self):
     super(TestGetChangedEntities, self).setUp()
     self.obj = Budget()
     self.account = Account()
     self.obj.be_accounts = [self.account]
     self.obj.clear_changed_entities()
     self.account2 = Account(id=self.account)
예제 #3
0
class TestGetChangedEntities(CommonTest):
    def setUp(self):
        super(TestGetChangedEntities, self).setUp()
        self.obj = Budget()
        self.account = Account()
        self.obj.be_accounts = [self.account]
        self.obj.clear_changed_entities()
        self.account2 = Account(id=self.account)

    def testgetChangedEntities_add(self):
        added_account = Account()
        self.obj.be_accounts.append(added_account)
        changed_entities = self.obj.get_changed_entities()
        self.assertEqual(changed_entities, {'be_accounts': [added_account]})

    def testgetChangedEntities_delete(self):
        self.obj.be_accounts.remove(self.account)
        changed_entities = self.obj.get_changed_entities()
        deleted = self.account.copy()
        deleted.is_tombstone = True
        self.assertEqual(changed_entities, {'be_accounts': [deleted]})

    def testgetChangedEntities_modify(self):
        self.account.account_name = 'BLA'
        changed_entities = self.obj.get_changed_entities()
        self.assertEqual(changed_entities, {'be_accounts': [self.account]})
예제 #4
0
    def setUp(self):
        super(TestGetChangedEntities, self).setUp()
        self.obj = Budget()
        self.account = Account()
        self.obj.be_accounts = [self.account]
        self.obj.clear_changed_entities()
        self.account2 = Account(id=self.account)

        self.client = factory.create_client(budget_name='budgetname',
                                            nynabconnection=MockConnection(),
                                            sync=False)
예제 #5
0
    def test_str(self):
        # tests no exceptions when getting the string representation of some entities
        obj = Transaction()
        obj.__str__()
        obj.__unicode__()

        obj2 = Budget()
        obj2.be_accounts.__str__()
예제 #6
0
    def testappendBad(self):
        obj = Budget()
        transaction = Transaction()

        def testappend():
            obj.be_accounts.append(transaction)

        self.assertRaises(ValueError, testappend)
예제 #7
0
class nYnabClient(object):
    def __init__(self, **kwargs):
        self.server_entities = {}
        self.budget_version_id = None
        self.logger = kwargs.get('logger', None)
        if self.logger is None:
            self.logger = get_logger()

        self.budget_name = kwargs.get('budgetname', None)
        if self.budget_name is None:
            logger.error('No budget name was provided')
            exit(-1)
        self.connection = kwargs.get('nynabconnection', None)
        self.catalog = Catalog()
        self.budget = Budget()
        self.budget_version = BudgetVersion()

        self.current_device_knowledge = {}
        self.device_knowledge_of_server = {}
        self.starting_device_knowledge = 0
        self.ending_device_knowledge = 0

        engine = kwargs.get('engine', create_engine('sqlite://'))

        Base.metadata.create_all(engine)
        self.Session = sessionmaker(bind=engine)

        self.session = self.Session()
        self.session.add(self.catalog)
        self.session.add(self.budget)
        self.session.commit()

        self.online = self.connection is not None
        self.catalogClient = RootObjClient(self.catalog, self, 'syncCatalogData')
        self.budgetClient = RootObjClient(self.budget, self, 'syncBudgetData')

    @staticmethod
    def from_obj(args, reset=False, sync=True, **kwargs):
        try:
            if not hasattr(args, 'logginglevel'):
                setattr(args, 'logginglevel', 'error')

            kwargs['logger'] = get_logger(args)
            kwargs['budgetname'] = args.budgetname
            kwargs['nynabconnection'] = nYnabConnection(args.email, args.password)
            if hasattr(args, 'engine'):
                kwargs['engine'] = args.engine

            client = nYnabClient(**kwargs)
            if sync:
                client.sync()
            if reset:
                # deletes the budget
                client.delete_budget(args.budgetname)
                client.create_budget(args.budgetname)
                client.select_budget(args.budgetname)
            return client
        except BudgetNotFound:
            print('No budget by the name %s found in nYNAB' % args.budgetname)
            exit(-1)

    @property
    def extra_catalog(self):
        return dict(user_id=self.connection.user_id)

    @property
    def extra_budget(self):
        return dict(calculated_entities_included=False, budget_version_id=self.budget_version_id)

    def sync_catalog(self):
        self.catalogClient.sync(extra=self.extra_catalog)

    def sync_budget(self):
        self.budgetClient.sync(extra=self.extra_budget)

    def sync(self):
        if self.connection is None:
            return
        self.logger.debug('Client.sync')

        self.sync_catalog()
        self.select_budget(self.budget_name)
        self.sync_budget()

        if self.budget_version_id is None and self.budget_name is not None:
            raise BudgetNotFound()

    def push_budget(self):
        self.budgetClient.push(extra=self.extra_budget)

    def push_catalog(self):
        self.catalogClient.push(extra=self.extra_catalog)

    def push(self, expected_delta=1):
        if self.connection is None:
            return
        # ending-starting represents the number of modifications that have been done to the data ?
        self.logger.debug('Client.push')

        catalog_changed_entities = self.catalog.get_changed_apidict()
        budget_changed_entities = self.budget.get_changed_apidict()

        delta = sum(len(l) for k, l in catalog_changed_entities.items()) + \
            sum(len(l) for k, l in budget_changed_entities.items())

        if delta != expected_delta:
            raise WrongPushException(expected_delta, delta)

        if any(catalog_changed_entities) or any(budget_changed_entities):
            self.ending_device_knowledge = self.starting_device_knowledge + 1

        self.push_catalog()
        self.push_budget()

        self.starting_device_knowledge = self.ending_device_knowledge
        self.session.commit()

    @operation(3)
    def add_account(self, account, balance, balance_date):
        payee = Payee(
            entities_account_id=account.id,
            enabled=True,
            auto_fill_subcategory_enabled=True,
            auto_fill_memo_enabled=False,
            auto_fill_amount_enabled=False,
            rename_on_import_enabled=False,
            name="Transfer : %s" % account.account_name
        )
        immediateincomeid = next(
            s.id for s in self.budget.be_subcategories if s.internal_name == 'Category/__ImmediateIncome__')
        startingbalanceid = next(p.id for p in self.budget.be_payees if p.internal_name == 'StartingBalancePayee')

        transaction = Transaction(
            accepted=True,
            amount=balance,
            entities_subcategory_id=immediateincomeid,
            cash_amount=0,
            cleared='Cleared',
            date=balance_date,
            entities_account_id=account.id,
            credit_amount=0,
            entities_payee_id=startingbalanceid,
            is_tombstone=False
        )

        self.budget.be_accounts.append(account)
        self.budget.be_payees.append(payee)
        self.budget.be_transactions.append(transaction)

    @operation(1)
    def delete_account(self, account):
        self.budget.be_accounts.remove(account)

    @operation(1)
    def add_transaction(self, transaction):
        self.budget.be_transactions.append(transaction)

    def add_transactions(self, transaction_list):
        for chunkelement in chunk(transaction_list, 50):
            self._add_transactions(chunkelement)

    @property
    def _add_transactions(self, transaction_list):
        @operation(len(transaction_list))
        def _add_transactions_method(self, transaction_list):
            for transaction in transaction_list:
                self.budget.be_transactions.append(transaction)
        return _add_transactions_method

    @operation(1)
    def delete_transaction(self, transaction):
        self.budget.be_transactions.remove(transaction)

    @operation(1)
    def delete_budget(self, budget_name):
        for budget in self.catalog.ce_budgets:
            if budget.budget_name == budget_name:
                self.catalog.ce_budgets.remove(budget)

    def select_budget(self, budget_name):
        self.budget_version_id = None
        for budget_version in self.catalog.ce_budget_versions:
            if budget_version.version_name == budget_name:
                self.budget_version_id = budget_version.id
        if self.budget_version_id is None:
            raise BudgetNotFound()

    def create_budget(self, budget_name):
        import json
        currency_format = dict(
            iso_code='USD',
            example_format='123,456.78',
            decimal_digits=2,
            decimal_separator='.',
            symbol_first=True,
            group_separator=',',
            currency_symbol='$',
            display_symbol=True
        )
        date_format = dict(
            format='MM/DD/YYYY'
        )
        self.connection.dorequest(opname='CreateNewBudget',
                                  request_dic={
                                      "budget_name": budget_name,
                                      "currency_format": json.dumps(currency_format),
                                      "date_format": json.dumps(date_format)
                                  })
예제 #8
0
class TestGetChangedEntities(CommonTest):
    def setUp(self):
        super(TestGetChangedEntities, self).setUp()
        self.obj = Budget()
        self.account = Account()
        self.obj.be_accounts = [self.account]
        self.obj.clear_changed_entities()
        self.account2 = Account(id=self.account)

        self.client = factory.create_client(budget_name='budgetname',
                                            nynabconnection=MockConnection(),
                                            sync=False)

    def testgetChangedEntities_add(self):
        added_account = Account()
        self.obj.be_accounts.append(added_account)
        changed_entities = self.obj.get_changed_entities()
        self.assertEqual(changed_entities, {'be_accounts': [added_account]})

    def testgetChangedEntities_addtransactionsubtransaction(self):
        added_transaction = Transaction()
        subtransaction1 = Subtransaction(
            entities_transaction=added_transaction)
        subtransaction2 = Subtransaction(
            entities_transaction=added_transaction)

        self.client.budget.be_transactions.append(added_transaction)
        self.client.budget.be_subtransactions.append(subtransaction1)
        self.client.budget.be_subtransactions.append(subtransaction2)

        self.client.session.commit()

        changed_entities = self.client.budget.get_changed_entities()
        self.assertIsInstance(changed_entities, dict)
        self.assertEqual(1, len(changed_entities.keys()))
        self.assertEqual('be_transaction_groups',
                         list(changed_entities.keys())[0])
        transaction_groups = changed_entities['be_transaction_groups']

        self.assertEqual(1, len(transaction_groups))
        self.assertEqual(added_transaction,
                         transaction_groups[0]['be_transaction'])

        self.assertIsNotNone(transaction_groups[0]['be_subtransactions'])
        try:
            self.assertItemsEqual(
                [subtransaction1, subtransaction2],
                set(transaction_groups[0]['be_subtransactions']))
        except AttributeError:
            self.assertCountEqual(
                [subtransaction1, subtransaction2],
                set(transaction_groups[0]['be_subtransactions']))

    def testgetChangedEntities_delete(self):
        self.obj.be_accounts.remove(self.account)
        changed_entities = self.obj.get_changed_entities()
        deleted = self.account.copy()
        deleted.is_tombstone = True
        self.assertEqual(changed_entities, {'be_accounts': [deleted]})

    def testgetChangedEntities_modify(self):
        self.account.account_name = 'BLA'
        changed_entities = self.obj.get_changed_entities()
        self.assertEqual(changed_entities, {'be_accounts': [self.account]})

    def test_arraytype(self):
        user = User()
        user.feature_flags = ['featureA', 'feature1']
        self.session.add(user)
        self.session.commit()

        fetched_user = self.session.query(User).first()
        self.assertEqual(user, fetched_user)
예제 #9
0
 def testappend(self):
     obj = Budget()
     account = Account()
     obj.be_accounts.append(account)
     self.assertEqual(len(obj.be_accounts), 1)
     self.assertEqual(list(obj.be_accounts)[-1], account)
예제 #10
0
 def __init__(self):
     self.id = '1234'
     self.user_id = '1234'
     self.catalog = Catalog()
     self.budget = Budget()
예제 #11
0
def test_append():
    obj = Budget()
    account = Account()
    obj.be_accounts.append(account)
    assert len(obj.be_accounts) == 1
    assert list(obj.be_accounts)[-1] == account
예제 #12
0
def objclient():
    client = Mock(spec=nYnabClient_)
    client.budget = Budget()
    obj_client = BudgetClient(client)
    return obj_client