[ledgercli] Store posting metadata
This commit is contained in:
		
							parent
							
								
									8abbe3462f
								
							
						
					
					
						commit
						86a6bec585
					
				
					 2 changed files with 74 additions and 16 deletions
				
			
		|  | @ -6,13 +6,10 @@ import os | ||||||
| import sys | import sys | ||||||
| import subprocess | import subprocess | ||||||
| import logging | import logging | ||||||
| import time |  | ||||||
| import re | import re | ||||||
| import pygit2 |  | ||||||
| 
 | 
 | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from xml.etree import ElementTree | from xml.etree import ElementTree | ||||||
| from contextlib import contextmanager |  | ||||||
| 
 | 
 | ||||||
| from accounting.exceptions import AccountingException, TransactionNotFound, \ | from accounting.exceptions import AccountingException, TransactionNotFound, \ | ||||||
|     LedgerNotBalanced, TransactionIDCollision |     LedgerNotBalanced, TransactionIDCollision | ||||||
|  | @ -21,6 +18,14 @@ from accounting.storage import Storage | ||||||
| 
 | 
 | ||||||
| _log = logging.getLogger(__name__) | _log = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | HAS_PYGIT = False | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     import pygit2 | ||||||
|  |     HAS_PYGIT = True | ||||||
|  | except ImportError: | ||||||
|  |     _log.warning('Failed to import pygit2') | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class Ledger(Storage): | class Ledger(Storage): | ||||||
|     def __init__(self, app=None, ledger_file=None, ledger_bin=None): |     def __init__(self, app=None, ledger_file=None, ledger_bin=None): | ||||||
|  | @ -34,6 +39,12 @@ class Ledger(Storage): | ||||||
|         self.ledger_file = ledger_file |         self.ledger_file = ledger_file | ||||||
|         _log.info('ledger file: %s', ledger_file) |         _log.info('ledger file: %s', ledger_file) | ||||||
| 
 | 
 | ||||||
|  |         self.init_pygit() | ||||||
|  | 
 | ||||||
|  |     def init_pygit(self): | ||||||
|  |         if not HAS_PYGIT: | ||||||
|  |             return False | ||||||
|  | 
 | ||||||
|         try: |         try: | ||||||
|             self.repository = pygit2.Repository( |             self.repository = pygit2.Repository( | ||||||
|                 os.path.join(os.path.dirname(self.ledger_file), '.git')) |                 os.path.join(os.path.dirname(self.ledger_file), '.git')) | ||||||
|  | @ -89,7 +100,7 @@ class Ledger(Storage): | ||||||
| 
 | 
 | ||||||
|         return args |         return args | ||||||
| 
 | 
 | ||||||
|     def send_command(self, command): |     def run_command(self, command): | ||||||
|         ''' |         ''' | ||||||
|         Creates a new ledger process with the specified :data:`command` and |         Creates a new ledger process with the specified :data:`command` and | ||||||
|         returns the output. |         returns the output. | ||||||
|  | @ -147,27 +158,34 @@ class Ledger(Storage): | ||||||
| 
 | 
 | ||||||
|         metadata_template = '   ;{0}: {1}\n' |         metadata_template = '   ;{0}: {1}\n' | ||||||
| 
 | 
 | ||||||
|         # TODO: Generate metadata for postings |  | ||||||
|         posting_template = ('  {account} {p.amount.symbol}' |         posting_template = ('  {account} {p.amount.symbol}' | ||||||
|                             ' {p.amount.amount}\n') |                             ' {p.amount.amount}\n') | ||||||
| 
 | 
 | ||||||
|         output = b'' |         output = b'' | ||||||
| 
 | 
 | ||||||
|  |         out_postings = '' | ||||||
|  | 
 | ||||||
|  |         for posting in transaction.postings: | ||||||
|  |             out_postings += posting_template.format( | ||||||
|  |                 p=posting, | ||||||
|  |                 account=posting.account + ' ' * ( | ||||||
|  |                     80 - (len(posting.account) + len(posting.amount.symbol) + | ||||||
|  |                           len(str(posting.amount.amount)) + 1 + 2)) | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             if len(posting.metadata): | ||||||
|  |                 for k, v in posting.metadata.items(): | ||||||
|  |                     out_postings += metadata_template.format(str(k), str(v)) | ||||||
|  | 
 | ||||||
|         # XXX: Even I hardly understands what this does, however I indent it it |         # XXX: Even I hardly understands what this does, however I indent it it | ||||||
|         # stays unreadable. |         # stays unreadable. | ||||||
|         output += transaction_template.format( |         output += transaction_template.format( | ||||||
|             date=transaction.date.strftime('%Y-%m-%d'), |             date=transaction.date.strftime('%Y-%m-%d'), | ||||||
|             t=transaction, |             t=transaction, | ||||||
|             metadata=''.join([ |             metadata=''.join([ | ||||||
|                 metadata_template.format(k, v) |                 metadata_template.format(str(k), str(v)) | ||||||
|                 for k, v in transaction.metadata.items()]), |                 for k, v in transaction.metadata.items()]), | ||||||
|             postings=''.join([posting_template.format( |             postings=out_postings | ||||||
|                 p=p, |  | ||||||
|                 account=p.account + ' ' * ( |  | ||||||
|                     80 - (len(p.account) + len(p.amount.symbol) + |  | ||||||
|                           len(str(p.amount.amount)) + 1 + 2) |  | ||||||
|                 )) for p in transaction.postings |  | ||||||
|             ]) |  | ||||||
|         ).encode('utf8') |         ).encode('utf8') | ||||||
| 
 | 
 | ||||||
|         with open(self.ledger_file, 'ab') as f: |         with open(self.ledger_file, 'ab') as f: | ||||||
|  | @ -194,7 +212,7 @@ class Ledger(Storage): | ||||||
|         return transaction.id |         return transaction.id | ||||||
| 
 | 
 | ||||||
|     def bal(self): |     def bal(self): | ||||||
|         output = self.send_command('xml') |         output = self.run_command('xml') | ||||||
| 
 | 
 | ||||||
|         if output is None: |         if output is None: | ||||||
|             raise RuntimeError('bal call returned no output') |             raise RuntimeError('bal call returned no output') | ||||||
|  | @ -248,7 +266,7 @@ class Ledger(Storage): | ||||||
|                 return transaction |                 return transaction | ||||||
| 
 | 
 | ||||||
|     def reg(self): |     def reg(self): | ||||||
|         output = self.send_command('xml') |         output = self.run_command('xml') | ||||||
| 
 | 
 | ||||||
|         if output is None: |         if output is None: | ||||||
|             raise RuntimeError('reg call returned no output') |             raise RuntimeError('reg call returned no output') | ||||||
|  |  | ||||||
|  | @ -293,8 +293,48 @@ class TransactionTestCase(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(response['error']['type'], 'TransactionNotFound') |         self.assertEqual(response['error']['type'], 'TransactionNotFound') | ||||||
| 
 | 
 | ||||||
|     def test_post_transaction_with_metadata(self): pass |     def test_post_transaction_with_metadata(self): | ||||||
|  |         transaction = copy.deepcopy(self.simple_transaction) | ||||||
| 
 | 
 | ||||||
|  |         transaction.metadata.update({'foo': 'bar'}) | ||||||
|  | 
 | ||||||
|  |         response = self._post_json('/transaction', transaction) | ||||||
|  | 
 | ||||||
|  |         transaction_id = response['transaction_ids'][0] | ||||||
|  | 
 | ||||||
|  |         response = self._get_json('/transaction/' + transaction_id) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |             response['transaction'].metadata, | ||||||
|  |             transaction.metadata) | ||||||
|  | 
 | ||||||
|  |     def test_post_transaction_with_posting_metadata(self): | ||||||
|  |         transaction = copy.deepcopy(self.simple_transaction) | ||||||
|  | 
 | ||||||
|  |         postings = [ | ||||||
|  |             Posting(account='Assets:Checking', metadata={'assets': 'checking'}, | ||||||
|  |                     amount=Amount(amount='-100.10', symbol='$')), | ||||||
|  |             Posting(account='Expenses:Foo', metadata={'expenses': 'foo'}, | ||||||
|  |                     amount=Amount(amount='100.10', symbol='$')) | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         transaction.postings = postings | ||||||
|  | 
 | ||||||
|  |         response = self._post_json('/transaction', transaction) | ||||||
|  | 
 | ||||||
|  |         transaction_id = response['transaction_ids'][0] | ||||||
|  | 
 | ||||||
|  |         response = self._get_json('/transaction/' + transaction_id) | ||||||
|  | 
 | ||||||
|  |         for posting in response['transaction'].postings: | ||||||
|  |             if posting.account == 'Expenses:Foo': | ||||||
|  |                 self.assertEqual(posting.metadata, {'expenses': 'foo'}) | ||||||
|  |             elif posting.account == 'Assets:Checking': | ||||||
|  |                 self.assertEqual(posting.metadata, {'assets': 'checking'}) | ||||||
|  |             else: | ||||||
|  |                 assert False, \ | ||||||
|  |                     'Something about this transaction\'s postings is' \ | ||||||
|  |                     ' unexpected' | ||||||
| 
 | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Joar Wandborg
						Joar Wandborg