예제 #1
0
 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)
예제 #2
0
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
예제 #3
0
 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)
예제 #4
0
 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)
예제 #5
0
 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)))
예제 #6
0
 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)))
예제 #7
0
 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))))
예제 #8
0
 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)))
예제 #9
0
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()