def test_filter_to_empty(self): # Test filtering down to empty. real_root = create_real([('Assets:US:Bank:Checking', '1 USD'), ('Assets:US:Bank:Savings', '2 USD'), ('Liabilities:Bank:CreditCard', '3 USD')]) real_copy = realization.filter(real_root, lambda ra0: False) self.assertTrue(real_copy is None)
def build_interesting_realacc(accapi, accounts): def is_included_account(realacc): for pattern in accounts: if re.match(pattern, realacc.account): if realacc.balance == inventory.Inventory(): return False # Remove empty accounts to "clean up" the tree return True return False realroot = accapi.realize() # first, filter out accounts that are not specified: realacc = realization.filter(realroot, is_included_account) if not realacc: sys.stderr.write( "No included accounts found. (Your --accounts <regex> failed to match any account)\n" ) sys.exit(1) # However, realacc includes all ancestor accounts of specified accounts, and their balances. For example, # if we specified 'Accounts:Investments:Brokerage', balances due to transactions on 'Accounts:Investments' # will also be included. We need to filter these out: for acc in realization.iter_children(realacc): if not is_included_account(acc): acc.balance = inventory.Inventory() return realacc
def render_real_text(self, real_root, options_map, file): if self.args.filter_expression: regexp = re.compile(self.args.filter_expression) real_root = realization.filter( real_root, lambda real_account: regexp.search(real_account.account)) if real_root: dformat = options_map['dcontext'].build(alignment=display_context.Align.DOT, reserved=2) realization.dump_balances(real_root, dformat, self.args.at_cost, True, file=file)
def render_real_text(self, real_root, options_map, file): if self.args.filter_expression: regexp = re.compile(self.args.filter_expression) real_root = realization.filter( real_root, lambda real_account: regexp.search(real_account.account)) if real_root: realization.dump_balances(real_root, self.args.at_cost, True, file=file)
def test_filter_no_leaves(self): # Test filtering that drops leaf nodes but that keeps intermediate # nodes. real_root = create_real([('Assets:US:Bank:Checking', '1 USD'), ('Assets:US:Bank:Savings', '2 USD'), ('Assets:US', '100 USD'), ('Assets', '100 USD')]) def ge100(ra0): return ra0.balance.get_currency_units('USD').number >= 100 real_copy = realization.filter(real_root, ge100) self.assertTrue(real_copy is not None) self.assertEqual({'Assets:US'}, set(ra0.account for ra0 in realization.iter_children(real_copy, True)))
def test_filter_almost_all(self): # Test filtering that culls leaves, to make sure that works. real_root = create_real([('Assets:US:Bank:Checking', '1 USD'), ('Assets:US:Bank:Savings', '2 USD'), ('Liabilities:USBank:CreditCard', '3 USD'), ('Assets', '100 USD'), ('Liabilities:US:Bank', '101 USD')]) def ge100(ra0): return ra0.balance.get_currency_units('USD').number >= 100 real_copy = realization.filter(real_root, ge100) self.assertTrue(real_copy is not None) self.assertEqual({'Assets', 'Liabilities:US:Bank'}, set(ra0.account for ra0 in realization.iter_children(real_copy, True)))
def test_filter_misc(self): real_root = create_real([('Assets:US:Bank:Checking', '1 USD'), ('Assets:US:Bank:Savings', '2 USD'), ('Assets:US:Cash', '3 USD'), ('Assets:CA:Cash', '4 USD'), ('Liabilities:Bank:CreditCard', '5 USD'), ('Expenses:Food:Grocery', '6 USD'), ('Expenses:Food:Restaurant', '7 USD'), ('Expenses:Food:Alcohol', '8 USD'), ('Expenses:Food', '10 USD')]) def even(real_account): return (not real_account.balance.is_empty() and real_account.balance.get_currency_units('NOK').number % 2 == 0) real_even = realization.filter(real_root, even) self.assertTrue(all(map(even, realization.iter_children(real_even, True))))
def test_filter_with_leaves(self): # Test filtering that keeps some leaf nodes with some intermediate nodes # that would otherwise be eliminated. real_root = create_real([('Assets:US:Bank:Checking', '1 USD'), ('Assets:US:Bank:Savings', '2 USD'), ('Liabilities:USBank:CreditCard', '3 USD')]) def not_empty(ra0): return not ra0.balance.is_empty() real_copy = realization.filter(real_root, not_empty) self.assertTrue(real_copy is not None) self.assertEqual({'Assets:US:Bank:Checking', 'Assets:US:Bank:Savings', 'Liabilities:USBank:CreditCard'}, set(ra0.account for ra0 in realization.iter_children(real_copy, True)))
def table_of_balances(real_root, operating_currencies, formatter, classes=None): """Render a tree table with the balance of each accounts. Args: real_root: A RealAccount node, the root node to render. operating_currencies: A list of strings, the operating currencies to render to their own dedicated columns. formatter: A object used to render account names and other links. classes: A list of strings, the CSS classes to attach to the renderd top-level table objet. Returns: A string with HTML contents, the rendered table. """ header = ['Account'] + operating_currencies + ['Other'] # Pre-calculate which accounts should be rendered. real_active = realization.filter(real_root, is_account_active) if real_active: active_set = { real_account.account for real_account in realization.iter_children(real_active) } else: active_set = set() balance_totals = inventory.Inventory() oss = io.StringIO() classes = list(classes) if classes else [] classes.append('fullwidth') for real_account, cells, row_classes in tree_table(oss, real_root, formatter, header, classes): if real_account is TOTALS_LINE: line_balance = balance_totals row_classes.append('totals') else: # Check if this account has had activity; if not, skip rendering it. if (real_account.account not in active_set and not account_types.is_root_account(real_account.account)): continue if real_account.account is None: row_classes.append('parent-node') # For each account line, get the final balance of the account (at cost). line_balance = real_account.balance.reduce(convert.get_cost) # Update the total balance for the totals line. balance_totals += line_balance # Extract all the positions that the user has identified as operating # currencies to their own subinventories. ccy_dict = line_balance.segregate_units(operating_currencies) # FIXME: This little algorithm is inefficient; rewrite it. for currency in operating_currencies: units = ccy_dict[currency].get_currency_units(currency) cells.append( formatter.render_number(units.number, units.currency ) if units.number != ZERO else '') # Render all the rest of the inventory in the last cell. if None in ccy_dict: ccy_balance = ccy_dict[None] last_cell = '<br/>'.join( formatter.render_amount(pos.units) for pos in sorted(ccy_balance)) else: last_cell = '' cells.append(last_cell) return oss.getvalue()