def context(self, ehash): try: entry = next(entry for entry in self.all_entries if ehash == compare.hash_entry(entry)) except StopIteration: return context_str = context.render_entry_context(self.all_entries, self.options, entry) return {"context": context_str.split("\n", 2)[2], "entry": entry}
def queries(self, query_hash=None): if not query_hash: return self._journal(self.all_entries, Query) matching_entries = [entry for entry in self.all_entries if query_hash == compare.hash_entry(entry)] if not matching_entries: return assert len(matching_entries) == 1 return serialize_entry(matching_entries[0])
def link_statements(entries, options_map): errors = [] if 'documents' not in options_map or len(options_map['documents']) == 0: return entries, errors all_documents = [(i, entry) for i, entry in enumerate(entries) if isinstance(entry, data.Document)] all_transactions = [(i, entry) for i, entry in enumerate(entries) if isinstance(entry, data.Transaction)] for i, entry in all_transactions: statements = [value for key, value in entry.meta.items() if key.startswith('statement')] _hash = hash_entry(entry)[:8] for statement in statements: statement_p = normpath(join(dirname(entry.meta['filename']), statement)) documents = [(j, document) for j, document in all_documents if (document.filename == statement_p) or (document.account in [pos.account for pos in entry.postings] and basename(document.filename) == statement)] if (len(documents) == 0): errors.append( StatementDocumentError( entry.meta, "Statement Document not found: {}".format(statement), entry)) continue for j, document in documents: tags = set(document.tags).union( ['statement']).difference(['discovered']) \ if document.tags else set(['statement']) links = set(document.links).union([_hash]) \ if document.links else set([_hash]) entries[j] = document._replace(links=links, tags=tags) links = set(entry.links).union([_hash]) \ if entry.links else set([_hash]) entries[i] = entry._replace(links=links) return entries, errors
def _add_metadata(new_entry, entry): new_entry['meta'] = { 'type': entry.__class__.__name__.lower(), } new_entry['hash'] = compare.hash_entry(entry) if entry.meta: new_entry['meta']['filename'] = getattr(entry.meta, 'filename', None) new_entry['meta']['lineno'] = getattr(entry.meta, 'lineno', None) new_entry['metadata'] = entry.meta.copy() new_entry['metadata'].pop("__tolerances__", None) new_entry['metadata'].pop("__automatic__", None) new_entry['metadata'].pop("filename", None) new_entry['metadata'].pop("lineno", None) return new_entry
def get_entry(self, entry_hash): """Find an entry. Arguments: entry_hash: Hash of the entry. Returns: The entry with the given hash. Raises: FavaAPIException: If there is no entry for the given hash. """ try: return next(entry for entry in self.all_entries if entry_hash == hash_entry(entry)) except StopIteration: raise FavaAPIException('No entry found for hash "{}"' .format(entry_hash))
def context(self, ehash): matching_entries = [entry for entry in self.all_entries if ehash == compare.hash_entry(entry)] if not matching_entries: return # the hash should uniquely identify the entry assert len(matching_entries) == 1 entry = matching_entries[0] context_str = context.render_entry_context(self.all_entries, self.options, entry) return { "hash": ehash, "context": context_str.split("\n", 2)[2], "filename": entry.meta["filename"], "lineno": entry.meta["lineno"], "journal": matching_entries, }
def link_documents(entries, _): errors = [] all_documents = [(index, entry) for index, entry in enumerate(entries) if isinstance(entry, data.Document)] transactions = [(index, entry) for index, entry in enumerate(entries) if isinstance(entry, data.Transaction)] for index, entry in transactions: disk_docs = [value for key, value in entry.meta.items() if key.startswith('document')] _hash = hash_entry(entry)[:8] for disk_doc in disk_docs: disk_doc_path = normpath(join(dirname(entry.meta['filename']), disk_doc)) documents = [(j, document) for j, document in all_documents if (document.filename == disk_doc_path) or (document.account in [pos.account for pos in entry.postings] and basename(document.filename) == disk_doc)] if not documents: errors.append( DocumentError( entry.meta, "Document not found: {}".format(disk_doc), entry)) continue for j, document in documents: tags = set(document.tags).union( ['linked']).difference(['discovered']) \ if document.tags else set(['linked']) links = set(document.links).union([_hash]) \ if document.links else set([_hash]) entries[j] = document._replace(links=links, tags=tags) links = set(entry.links).union([_hash]) \ if entry.links else set([_hash]) entries[index] = entry._replace(links=links) return entries, errors
def context(self, ehash): matching_entries = [entry for entry in self.all_entries if ehash == compare.hash_entry(entry)] if not matching_entries: return # the hash should uniquely identify the entry assert len(matching_entries) == 1 entry = matching_entries[0] context_str = context.render_entry_context(self.all_entries, self.options, entry) return { 'hash': ehash, 'context': context_str.split("\n", 2)[2], 'filename': entry.meta['filename'], 'lineno': entry.meta['lineno'], 'journal': self._journal(matching_entries), }
def context(self, ehash=None): matching_entries = [entry for entry in self.entries if ehash == compare.hash_entry(entry)] contexts = [] dcontext = self.options['dcontext'] for entry in matching_entries: context_str = context.render_entry_context( self.entries, self.options, entry) hash_ = context_str.split("\n",2)[0].split(':')[1].strip() filenamelineno = context_str.split("\n",2)[1] filename = filenamelineno.split(":")[1].strip() lineno = int(filenamelineno.split(":")[2].strip()) contexts.append({ 'hash': hash_, 'context': context_str.split("\n",2)[2], 'filename': filename, 'line': lineno }) # TODO # if len(matching_entries) == 0: # print("ERROR: Could not find matching entry for '{}'".format(ehash), # file=oss) # # elif len(matching_entries) > 1: # print("ERROR: Ambiguous entries for '{}'".format(ehash), # file=oss) # print(file=oss) # dcontext = app.options['dcontext'] # printer.print_entries(matching_entries, dcontext, file=oss) # # else: return { 'hash': ehash, 'contexts': contexts, 'journal': self._journal_for_postings(matching_entries) }
def context(self, ehash): matching_entries = [entry for entry in self.all_entries if ehash == compare.hash_entry(entry)] if not matching_entries: return # the hash should uniquely identify the entry assert len(matching_entries) == 1 entry = matching_entries[0] context_str = context.render_entry_context(self.all_entries, self.options, entry) ctx = context_str.split("\n", 2) filenamelineno = ctx[1] filename = filenamelineno.split(":")[1].strip() lineno = int(filenamelineno.split(":")[2].strip()) return { 'hash': ehash, 'context': ctx[2], 'filename': filename, 'line': lineno, 'journal': self._journal(matching_entries) }
def hash_entry(self, entry): return compare.hash_entry(entry)
def _journal_for_postings(self, postings, include_types=None, with_change_and_balance=False): journal = [] for posting, leg_postings, change, entry_balance in realization.iterate_with_balance(postings): if include_types and not isinstance(posting, include_types): continue if isinstance(posting, Transaction) or \ isinstance(posting, Note) or \ isinstance(posting, Balance) or \ isinstance(posting, Open) or \ isinstance(posting, Close) or \ isinstance(posting, Pad) or \ isinstance(posting, Event) or \ isinstance(posting, Document): entry = { 'meta': { 'type': posting.__class__.__name__.lower(), 'filename': posting.meta['filename'], 'lineno': posting.meta['lineno'] }, 'date': posting.date, 'hash': compare.hash_entry(posting), 'metadata': posting.meta.copy() } entry['metadata'].pop("__tolerances__", None) entry['metadata'].pop("filename", None) entry['metadata'].pop("lineno", None) if isinstance(posting, Open): entry['account'] = posting.account entry['currencies'] = posting.currencies if isinstance(posting, Close): entry['account'] = posting.account if isinstance(posting, Event): entry['type'] = posting.type entry['description'] = posting.description if isinstance(posting, Note): entry['comment'] = posting.comment if isinstance(posting, Document): entry['account'] = posting.account entry['filename'] = posting.filename if isinstance(posting, Pad): entry['account'] = posting.account entry['source_account'] = posting.source_account if isinstance(posting, Balance): entry['account'] = posting.account entry['change'] = { posting.amount.currency: posting.amount.number } entry['amount'] = { posting.amount.currency: posting.amount.number } if posting.diff_amount: balance = entry_balance.get_units(posting.amount.currency) entry['diff_amount'] = { posting.diff_amount.currency: posting.diff_amount.number } entry['balance'] = { balance.currency: balance.number } if isinstance(posting, Transaction): if posting.flag == 'P': entry['meta']['type'] = 'padding' # TODO handle Padding, Summarize and Transfer entry['flag'] = posting.flag entry['payee'] = posting.payee entry['narration'] = posting.narration entry['tags'] = posting.tags or [] entry['links'] = posting.links or [] entry['legs'] = [] for posting_ in posting.postings: leg = { 'account': posting_.account, 'flag': posting_.flag, 'hash': entry['hash'] } if posting_.position: leg['position'] = posting_.position.number leg['position_currency'] = posting_.position.lot.currency cost = interpolate.get_posting_weight(posting_) leg['cost'] = cost.number leg['cost_currency'] = cost.currency if posting_.price: leg['price'] = posting_.price.number leg['price_currency'] = posting_.price.currency entry['legs'].append(leg) if with_change_and_balance: if isinstance(posting, Balance): entry['change'] = { posting.amount.currency: posting.amount.number } entry['balance'] = self._inventory_to_json(entry_balance) #, include_currencies=entry['change'].keys()) if isinstance(posting, Transaction): entry['change'] = self._inventory_to_json(change) entry['balance'] = self._inventory_to_json(entry_balance, include_currencies=entry['change'].keys()) journal.append(entry) return journal
def _journal_for_postings(self, postings, include_types=None): journal = [] for posting, leg_postings, change, entry_balance in realization.iterate_with_balance(postings): if include_types and not isinstance(posting, include_types): continue if isinstance(posting, Transaction) or \ isinstance(posting, Note) or \ isinstance(posting, Balance) or \ isinstance(posting, Open) or \ isinstance(posting, Close) or \ isinstance(posting, Pad) or \ isinstance(posting, Document): # TEMP # if isinstance(posting, TxnPosting): # posting = posting.txn entry = { 'meta': { 'type': posting.__class__.__name__.lower(), 'filename': posting.meta['filename'], 'lineno': posting.meta['lineno'] }, 'date': posting.date, 'hash': compare.hash_entry(posting) } if isinstance(posting, Open): entry['account'] = posting.account entry['currencies'] = posting.currencies entry['booking'] = posting.booking # TODO im html-template if isinstance(posting, Close): entry['account'] = posting.account if isinstance(posting, Note): entry['comment'] = posting.comment if isinstance(posting, Document): entry['account'] = posting.account entry['filename'] = posting.filename if isinstance(posting, Pad): entry['account'] = posting.account entry['source_account'] = posting.source_account if isinstance(posting, Balance): # TODO failed balances entry['account'] = posting.account entry['change'] = { posting.amount.currency: posting.amount.number } entry['balance'] = { posting.amount.currency: posting.amount.number } entry['tolerance'] = posting.tolerance # TODO currency? TODO in HTML-template entry['diff_amount'] = posting.diff_amount # TODO currency? TODO in HTML-template if isinstance(posting, Transaction): if posting.flag == 'P': entry['meta']['type'] = 'padding' entry['flag'] = posting.flag entry['payee'] = posting.payee entry['narration'] = posting.narration entry['tags'] = posting.tags entry['links'] = posting.links entry['change'] = self._inventory_to_json(change) entry['balance'] = self._inventory_to_json(entry_balance) entry['legs'] = [] for posting_ in posting.postings: leg = { 'account': posting_.account, 'flag': posting_.flag, 'hash': entry['hash'] } if posting_.position: leg['position'] = posting_.position.number leg['position_currency'] = posting_.position.lot.currency if posting_.price: leg['price'] = posting_.price.number leg['price_currency'] = posting_.price.currency entry['legs'].append(leg) journal.append(entry) return journal
def hash_entry(entry): """Hash an entry.""" return compare.hash_entry(entry)