[storage] Added delete_transaction method
- Added storage.ledgercli implementation - Added storage.ledgercli update_transaction - Added storage.ledgercli get_transaction - Pushing pre-built docs
This commit is contained in:
parent
c80955f199
commit
02fc05aebd
8 changed files with 191 additions and 14 deletions
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
from accounting.exceptions import AccountingException
|
||||||
|
|
||||||
class Storage():
|
|
||||||
|
class Storage:
|
||||||
'''
|
'''
|
||||||
ABC for accounting storage
|
ABC for accounting storage
|
||||||
'''
|
'''
|
||||||
|
@ -38,6 +40,14 @@ class Storage():
|
||||||
def update_transaction(self, transaction):
|
def update_transaction(self, transaction):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def delete_transaction(self, transaction_id):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def reverse_transaction(self, transaction_id):
|
def reverse_transaction(self, transaction_id):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionNotFound(AccountingException):
|
||||||
|
pass
|
||||||
|
|
|
@ -6,13 +6,15 @@ import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from accounting.exceptions import AccountingException
|
||||||
from accounting.models import Account, Transaction, Posting, Amount
|
from accounting.models import Account, Transaction, Posting, Amount
|
||||||
from accounting.storage import Storage
|
from accounting.storage import Storage, TransactionNotFound
|
||||||
|
|
||||||
_log = logging.getLogger(__name__)
|
_log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -205,6 +207,8 @@ class Ledger(Storage):
|
||||||
|
|
||||||
_log.debug('written to file: %s', output)
|
_log.debug('written to file: %s', output)
|
||||||
|
|
||||||
|
return transaction.id
|
||||||
|
|
||||||
def bal(self):
|
def bal(self):
|
||||||
output = self.send_command('xml')
|
output = self.send_command('xml')
|
||||||
|
|
||||||
|
@ -252,6 +256,16 @@ class Ledger(Storage):
|
||||||
def get_transactions(self):
|
def get_transactions(self):
|
||||||
return self.reg()
|
return self.reg()
|
||||||
|
|
||||||
|
def get_transaction(self, transaction_id):
|
||||||
|
transactions = self.get_transactions()
|
||||||
|
|
||||||
|
for transaction in transactions:
|
||||||
|
if transaction.id == transaction_id:
|
||||||
|
return transaction
|
||||||
|
|
||||||
|
raise TransactionNotFound('No transaction with id %s found',
|
||||||
|
transaction_id)
|
||||||
|
|
||||||
def reg(self):
|
def reg(self):
|
||||||
output = self.send_command('xml')
|
output = self.send_command('xml')
|
||||||
|
|
||||||
|
@ -314,8 +328,111 @@ class Ledger(Storage):
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
|
def delete_transaction(self, transaction_id):
|
||||||
|
'''
|
||||||
|
Delete a transaction from the ledger file.
|
||||||
|
|
||||||
|
This method opens the ledger file, loads all lines into memory and
|
||||||
|
looks for the transaction_id, then looks for the bounds of that
|
||||||
|
transaction in the ledger file, removes all lines within the bounds of
|
||||||
|
the transaction and removes them, then writes the lines back to the
|
||||||
|
ledger file.
|
||||||
|
|
||||||
|
Exceptions:
|
||||||
|
|
||||||
|
- RuntimeError: If all boundaries to the transaction are not found
|
||||||
|
- TransactionNotFound: If no such transaction_id can be found in
|
||||||
|
:data:`self.ledger_file`
|
||||||
|
'''
|
||||||
|
f = open(self.ledger_file, 'r')
|
||||||
|
|
||||||
|
lines = [i for i in f]
|
||||||
|
|
||||||
|
# A mapping of line meanings and their line numbers as found by the
|
||||||
|
# following logic
|
||||||
|
semantic_lines = dict(
|
||||||
|
id_location=None,
|
||||||
|
transaction_start=None,
|
||||||
|
next_transaction_or_eof=None
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if transaction_id in line:
|
||||||
|
semantic_lines['id_location'] = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if not semantic_lines['id_location']:
|
||||||
|
raise TransactionNotFound('No transaction with ID "%s" found')
|
||||||
|
|
||||||
|
transaction_start_pattern = re.compile(r'^\S')
|
||||||
|
|
||||||
|
cursor = semantic_lines['id_location'] - 1
|
||||||
|
|
||||||
|
# Find the first line of the transaction
|
||||||
|
while True:
|
||||||
|
if transaction_start_pattern.match(lines[cursor]):
|
||||||
|
semantic_lines['transaction_start'] = cursor
|
||||||
|
break
|
||||||
|
|
||||||
|
cursor -= 1
|
||||||
|
|
||||||
|
cursor = semantic_lines['id_location'] + 1
|
||||||
|
|
||||||
|
# Find the last line of the transaction
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
if transaction_start_pattern.match(lines[cursor]):
|
||||||
|
semantic_lines['next_transaction_or_eof'] = cursor
|
||||||
|
break
|
||||||
|
except IndexError:
|
||||||
|
# Set next_line_without_starting_space_or_end_of_file to
|
||||||
|
# the cursor. The cursor will be an index not included in the
|
||||||
|
# list of lines
|
||||||
|
semantic_lines['next_transaction_or_eof'] = cursor
|
||||||
|
break
|
||||||
|
|
||||||
|
cursor += 1
|
||||||
|
|
||||||
|
if not all(map(lambda v: v is not None, semantic_lines.values())):
|
||||||
|
raise RuntimeError('Could not find all the values necessary for'
|
||||||
|
' safe deletion of a transaction.')
|
||||||
|
|
||||||
|
del_start = semantic_lines['transaction_start']
|
||||||
|
|
||||||
|
if len(lines) == semantic_lines['next_transaction_or_eof']:
|
||||||
|
_log.debug('There are no transactions below the transaction being'
|
||||||
|
' deleted. The line before the first line of the'
|
||||||
|
' transaction will be deleted.')
|
||||||
|
# Delete the preceding line to make the file
|
||||||
|
del_start -= 1
|
||||||
|
|
||||||
|
del lines[del_start:semantic_lines['next_transaction_or_eof']]
|
||||||
|
|
||||||
|
with open(self.ledger_file, 'w') as f:
|
||||||
|
for line in lines:
|
||||||
|
f.write(line)
|
||||||
|
|
||||||
def update_transaction(self, transaction):
|
def update_transaction(self, transaction):
|
||||||
_log.debug('DUMMY: Updated transaction: %s', transaction)
|
'''
|
||||||
|
Update a transaction in the ledger file.
|
||||||
|
|
||||||
|
Takes a :class:`~accounting.models.Transaction` object and removes
|
||||||
|
the old transaction using :data:`transaction.id` from the passed
|
||||||
|
:class:`~accounting.models.Transaction` instance and adds
|
||||||
|
:data:`transaction` to the database.
|
||||||
|
'''
|
||||||
|
if not transaction.id:
|
||||||
|
return AccountingException('The transaction %s has no'
|
||||||
|
' id attribute', transaction)
|
||||||
|
|
||||||
|
old_transaction = self.get_transaction(transaction.id)
|
||||||
|
|
||||||
|
self.delete_transaction(transaction.id)
|
||||||
|
|
||||||
|
self.add_transaction(transaction)
|
||||||
|
|
||||||
|
_log.debug('Updated transaction from: %s to: %s', old_transaction,
|
||||||
|
transaction)
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
|
|
|
@ -11,7 +11,6 @@ import logging
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from flask import Flask, jsonify, request
|
from flask import Flask, jsonify, request
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
|
||||||
from flask.ext.script import Manager
|
from flask.ext.script import Manager
|
||||||
from flask.ext.migrate import Migrate, MigrateCommand
|
from flask.ext.migrate import Migrate, MigrateCommand
|
||||||
|
|
||||||
|
@ -65,6 +64,7 @@ def transaction_get():
|
||||||
'''
|
'''
|
||||||
return jsonify(transactions=storage.get_transactions())
|
return jsonify(transactions=storage.get_transactions())
|
||||||
|
|
||||||
|
|
||||||
@app.route('/transaction/<string:transaction_id>', methods=['POST'])
|
@app.route('/transaction/<string:transaction_id>', methods=['POST'])
|
||||||
@jsonify_exceptions
|
@jsonify_exceptions
|
||||||
def transaction_update(transaction_id=None):
|
def transaction_update(transaction_id=None):
|
||||||
|
@ -74,8 +74,8 @@ def transaction_update(transaction_id=None):
|
||||||
transaction = request.json['transaction']
|
transaction = request.json['transaction']
|
||||||
|
|
||||||
if transaction.id is not None and not transaction.id == transaction_id:
|
if transaction.id is not None and not transaction.id == transaction_id:
|
||||||
raise AccountingException('The transaction data has an ID attribute and'
|
raise AccountingException('The transaction data has an ID attribute'
|
||||||
' it is not the same ID as in the path')
|
' and it is not the same ID as in the path')
|
||||||
elif transaction.id is None:
|
elif transaction.id is None:
|
||||||
transaction.id = transaction_id
|
transaction.id = transaction_id
|
||||||
|
|
||||||
|
@ -84,6 +84,17 @@ def transaction_update(transaction_id=None):
|
||||||
return jsonify(status='OK')
|
return jsonify(status='OK')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/transaction/<string:transaction_id>', methods=['DELETE'])
|
||||||
|
@jsonify_exceptions
|
||||||
|
def transaction_delete(transaction_id=None):
|
||||||
|
if transaction_id is None:
|
||||||
|
raise AccountingException('Transaction ID cannot be None')
|
||||||
|
|
||||||
|
storage.delete_transaction(transaction_id)
|
||||||
|
|
||||||
|
return jsonify(status='OK')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/transaction', methods=['POST'])
|
@app.route('/transaction', methods=['POST'])
|
||||||
@jsonify_exceptions
|
@jsonify_exceptions
|
||||||
def transaction_post():
|
def transaction_post():
|
||||||
|
@ -139,10 +150,12 @@ def transaction_post():
|
||||||
if not transactions:
|
if not transactions:
|
||||||
raise AccountingException('No transaction data provided')
|
raise AccountingException('No transaction data provided')
|
||||||
|
|
||||||
for transaction in transactions:
|
transaction_ids = []
|
||||||
storage.add_transaction(transaction)
|
|
||||||
|
|
||||||
return jsonify(status='OK')
|
for transaction in transactions:
|
||||||
|
transaction_ids.append(storage.add_transaction(transaction))
|
||||||
|
|
||||||
|
return jsonify(status='OK', transaction_ids=transaction_ids)
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
|
|
5
doc/build/html/api/accounting.html
vendored
5
doc/build/html/api/accounting.html
vendored
|
@ -285,6 +285,11 @@ and the Flask endpoints.</p>
|
||||||
<tt class="descclassname">accounting.web.</tt><tt class="descname">main</tt><big>(</big><em>argv=None</em><big>)</big><a class="reference internal" href="../_modules/accounting/web.html#main"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.web.main" title="Permalink to this definition">¶</a></dt>
|
<tt class="descclassname">accounting.web.</tt><tt class="descname">main</tt><big>(</big><em>argv=None</em><big>)</big><a class="reference internal" href="../_modules/accounting/web.html#main"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.web.main" title="Permalink to this definition">¶</a></dt>
|
||||||
<dd></dd></dl>
|
<dd></dd></dl>
|
||||||
|
|
||||||
|
<dl class="function">
|
||||||
|
<dt id="accounting.web.transaction_delete">
|
||||||
|
<tt class="descclassname">accounting.web.</tt><tt class="descname">transaction_delete</tt><big>(</big><em>transaction_id=None</em><big>)</big><a class="headerlink" href="#accounting.web.transaction_delete" title="Permalink to this definition">¶</a></dt>
|
||||||
|
<dd></dd></dl>
|
||||||
|
|
||||||
<dl class="function">
|
<dl class="function">
|
||||||
<dt id="accounting.web.transaction_get">
|
<dt id="accounting.web.transaction_get">
|
||||||
<tt class="descclassname">accounting.web.</tt><tt class="descname">transaction_get</tt><big>(</big><big>)</big><a class="reference internal" href="../_modules/accounting/web.html#transaction_get"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.web.transaction_get" title="Permalink to this definition">¶</a></dt>
|
<tt class="descclassname">accounting.web.</tt><tt class="descname">transaction_get</tt><big>(</big><big>)</big><a class="reference internal" href="../_modules/accounting/web.html#transaction_get"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.web.transaction_get" title="Permalink to this definition">¶</a></dt>
|
||||||
|
|
26
doc/build/html/api/accounting.storage.html
vendored
26
doc/build/html/api/accounting.storage.html
vendored
|
@ -102,7 +102,19 @@ based on <tt class="xref py py-attr docutils literal"><span class="pre">self.led
|
||||||
<dl class="method">
|
<dl class="method">
|
||||||
<dt id="accounting.storage.ledgercli.Ledger.delete_transaction">
|
<dt id="accounting.storage.ledgercli.Ledger.delete_transaction">
|
||||||
<tt class="descname">delete_transaction</tt><big>(</big><em>transaction_id</em><big>)</big><a class="headerlink" href="#accounting.storage.ledgercli.Ledger.delete_transaction" title="Permalink to this definition">¶</a></dt>
|
<tt class="descname">delete_transaction</tt><big>(</big><em>transaction_id</em><big>)</big><a class="headerlink" href="#accounting.storage.ledgercli.Ledger.delete_transaction" title="Permalink to this definition">¶</a></dt>
|
||||||
<dd></dd></dl>
|
<dd><p>Delete a transaction from the ledger file.</p>
|
||||||
|
<p>This method opens the ledger file, loads all lines into memory and
|
||||||
|
looks for the transaction_id, then looks for the bounds of that
|
||||||
|
transaction in the ledger file, removes all lines within the bounds of
|
||||||
|
the transaction and removes them, then writes the lines back to the
|
||||||
|
ledger file.</p>
|
||||||
|
<p>Exceptions:</p>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>RuntimeError: If all boundaries to the transaction are not found</li>
|
||||||
|
<li>TransactionNotFound: If no such transaction_id can be found in
|
||||||
|
<tt class="xref py py-data docutils literal"><span class="pre">self.ledger_file</span></tt></li>
|
||||||
|
</ul>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
<dl class="method">
|
<dl class="method">
|
||||||
<dt id="accounting.storage.ledgercli.Ledger.get_process">
|
<dt id="accounting.storage.ledgercli.Ledger.get_process">
|
||||||
|
@ -168,7 +180,12 @@ without the prompt.</p>
|
||||||
<dl class="method">
|
<dl class="method">
|
||||||
<dt id="accounting.storage.ledgercli.Ledger.update_transaction">
|
<dt id="accounting.storage.ledgercli.Ledger.update_transaction">
|
||||||
<tt class="descname">update_transaction</tt><big>(</big><em>transaction</em><big>)</big><a class="reference internal" href="../_modules/accounting/storage/ledgercli.html#Ledger.update_transaction"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.storage.ledgercli.Ledger.update_transaction" title="Permalink to this definition">¶</a></dt>
|
<tt class="descname">update_transaction</tt><big>(</big><em>transaction</em><big>)</big><a class="reference internal" href="../_modules/accounting/storage/ledgercli.html#Ledger.update_transaction"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.storage.ledgercli.Ledger.update_transaction" title="Permalink to this definition">¶</a></dt>
|
||||||
<dd></dd></dl>
|
<dd><p>Update a transaction in the ledger file.</p>
|
||||||
|
<p>Takes a <a class="reference internal" href="accounting.html#accounting.models.Transaction" title="accounting.models.Transaction"><tt class="xref py py-class docutils literal"><span class="pre">Transaction</span></tt></a> object and removes
|
||||||
|
the old transaction using <tt class="xref py py-data docutils literal"><span class="pre">transaction.id</span></tt> from the passed
|
||||||
|
<a class="reference internal" href="accounting.html#accounting.models.Transaction" title="accounting.models.Transaction"><tt class="xref py py-class docutils literal"><span class="pre">Transaction</span></tt></a> instance and adds
|
||||||
|
<tt class="xref py py-data docutils literal"><span class="pre">transaction</span></tt> to the database.</p>
|
||||||
|
</dd></dl>
|
||||||
|
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
|
|
||||||
|
@ -190,6 +207,11 @@ without the prompt.</p>
|
||||||
<tt class="descname">add_transaction</tt><big>(</big><em>transaction</em><big>)</big><a class="headerlink" href="#accounting.storage.Storage.add_transaction" title="Permalink to this definition">¶</a></dt>
|
<tt class="descname">add_transaction</tt><big>(</big><em>transaction</em><big>)</big><a class="headerlink" href="#accounting.storage.Storage.add_transaction" title="Permalink to this definition">¶</a></dt>
|
||||||
<dd></dd></dl>
|
<dd></dd></dl>
|
||||||
|
|
||||||
|
<dl class="method">
|
||||||
|
<dt id="accounting.storage.Storage.delete_transaction">
|
||||||
|
<tt class="descname">delete_transaction</tt><big>(</big><em>transaction_id</em><big>)</big><a class="headerlink" href="#accounting.storage.Storage.delete_transaction" title="Permalink to this definition">¶</a></dt>
|
||||||
|
<dd></dd></dl>
|
||||||
|
|
||||||
<dl class="method">
|
<dl class="method">
|
||||||
<dt id="accounting.storage.Storage.get_account">
|
<dt id="accounting.storage.Storage.get_account">
|
||||||
<tt class="descname">get_account</tt><big>(</big><em>*args</em>, <em>**kw</em><big>)</big><a class="reference internal" href="../_modules/accounting/storage.html#Storage.get_account"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.storage.Storage.get_account" title="Permalink to this definition">¶</a></dt>
|
<tt class="descname">get_account</tt><big>(</big><em>*args</em>, <em>**kw</em><big>)</big><a class="reference internal" href="../_modules/accounting/storage.html#Storage.get_account"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#accounting.storage.Storage.get_account" title="Permalink to this definition">¶</a></dt>
|
||||||
|
|
14
doc/build/html/genindex.html
vendored
14
doc/build/html/genindex.html
vendored
|
@ -243,6 +243,12 @@
|
||||||
<dt><a href="api/accounting.storage.html#accounting.storage.ledgercli.Ledger.delete_transaction">delete_transaction() (accounting.storage.ledgercli.Ledger method)</a>
|
<dt><a href="api/accounting.storage.html#accounting.storage.ledgercli.Ledger.delete_transaction">delete_transaction() (accounting.storage.ledgercli.Ledger method)</a>
|
||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
|
<dd><dl>
|
||||||
|
|
||||||
|
<dt><a href="api/accounting.storage.html#accounting.storage.Storage.delete_transaction">(accounting.storage.Storage method)</a>
|
||||||
|
</dt>
|
||||||
|
|
||||||
|
</dl></dd>
|
||||||
|
|
||||||
<dt><a href="api/accounting.html#accounting.transport.AccountingDecoder.dict_to_object">dict_to_object() (accounting.transport.AccountingDecoder method)</a>
|
<dt><a href="api/accounting.html#accounting.transport.AccountingDecoder.dict_to_object">dict_to_object() (accounting.transport.AccountingDecoder method)</a>
|
||||||
</dt>
|
</dt>
|
||||||
|
@ -546,16 +552,20 @@
|
||||||
|
|
||||||
</dl></dd>
|
</dl></dd>
|
||||||
|
|
||||||
<dt><a href="api/accounting.html#accounting.web.transaction_get">transaction_get() (in module accounting.web)</a>
|
<dt><a href="api/accounting.html#accounting.web.transaction_delete">transaction_delete() (in module accounting.web)</a>
|
||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
|
|
||||||
<dt><a href="api/accounting.html#accounting.web.transaction_post">transaction_post() (in module accounting.web)</a>
|
<dt><a href="api/accounting.html#accounting.web.transaction_get">transaction_get() (in module accounting.web)</a>
|
||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
</dl></td>
|
</dl></td>
|
||||||
<td style="width: 33%" valign="top"><dl>
|
<td style="width: 33%" valign="top"><dl>
|
||||||
|
|
||||||
|
<dt><a href="api/accounting.html#accounting.web.transaction_post">transaction_post() (in module accounting.web)</a>
|
||||||
|
</dt>
|
||||||
|
|
||||||
|
|
||||||
<dt><a href="api/accounting.html#accounting.web.transaction_update">transaction_update() (in module accounting.web)</a>
|
<dt><a href="api/accounting.html#accounting.web.transaction_update">transaction_update() (in module accounting.web)</a>
|
||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
|
|
BIN
doc/build/html/objects.inv
vendored
BIN
doc/build/html/objects.inv
vendored
Binary file not shown.
2
doc/build/html/searchindex.js
vendored
2
doc/build/html/searchindex.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue