def group_accounts_by_metadata(accounts_map, meta_name): """Group accounts by the value of a metadata field on its corresponding Open entry or in one of its parent accounts. Args: accounts_map: A mapping of account name to its Open entry. meta_name: A string, the name of a metadata key to extract to figure out the group name. Returns: A dict of group names (the values of the metadata field) to a list of account name strings, and a list of ignored accounts. """ groups = collections.defaultdict(list) ignored_accounts = set() for account_ in accounts_map: # Find the group of this account; the group is defined as the first # parent account that has a particular metadata field. If an account is # not covered by a parent with the metadata, it defines its own group. for parent_account in account.parents(account_): open_entry = accounts_map.get(parent_account, None) if (open_entry and open_entry.meta and meta_name in open_entry.meta): group = open_entry.meta[meta_name] groups[group].append(account_) break else: ignored_accounts.add(account_) for group in groups.values(): group.sort() return dict(groups), ignored_accounts
def create_report(entries, options_map): real_root = realization.realize(entries) # Find the institutions from the data. groups, ignored_accounts = find_institutions(entries, options_map) # List all the asset accounts which aren't included in the report. oc_map = getters.get_account_open_close(entries) open_map = {acc: open_entry for acc, (open_entry, _) in oc_map.items()} for acc in sorted(ignored_accounts): logging.info("Ignored account: %s", acc) # Gather missing fields and create a report object. institutions = [] for name, accounts in sorted(groups.items()): # Get the institution fields, which is the union of the fields for all # the accounts with the institution fields. institution_accounts = [ acc for acc in accounts if 'institution' in open_map[acc].meta ] institution_fields = {} for acc in institution_accounts: for key, value in open_map[acc].meta.items(): institution_fields.setdefault(key, value) institution_fields.pop('filename', None) institution_fields.pop('lineno', None) # Create infos for each account in this institution. account_reports = [] for acc in accounts: account_fields = {} for subacc in account.parents(acc): open_entry = open_map[subacc] if 'institution' in open_entry.meta: break account_fields.update(open_entry.meta) account_fields.pop('filename', None) account_fields.pop('lineno', None) for field in institution_fields: account_fields.pop(field, None) real_node = realization.get(real_root, acc) account_reports.append( AccountReport( acc, open_entry.date, real_node.balance, sum(1 for posting in real_node.txn_postings if isinstance(posting, data.TxnPosting)), account_fields)) # Create the institution report. institution = InstitutionReport(name, institution_fields, account_reports) institutions.append(institution) return Report(options_map['title'], institutions)
def test_parents(self): iterator = account.parents('Assets:Bank:Checking') self.assertIsInstance(iterator, types.GeneratorType) self.assertEqual(['Assets:Bank:Checking', 'Assets:Bank', 'Assets'], list(iterator))