diff options
-rw-r--r-- | autoxact.cc | 24 | ||||
-rw-r--r-- | autoxact.h | 6 | ||||
-rw-r--r-- | journal.cc | 121 | ||||
-rw-r--r-- | ledger.el | 20 | ||||
-rw-r--r-- | ledger.h | 31 | ||||
-rw-r--r-- | main.cc | 3 | ||||
-rw-r--r-- | python.cc | 2 | ||||
-rw-r--r-- | textual.cc | 102 | ||||
-rw-r--r-- | textual.h | 29 |
9 files changed, 191 insertions, 147 deletions
diff --git a/autoxact.cc b/autoxact.cc index cc07ad2f..4de8122d 100644 --- a/autoxact.cc +++ b/autoxact.cc @@ -2,10 +2,10 @@ namespace ledger { -void automated_transaction_t::extend_entry(entry_t * entry) +void automated_transaction_t::extend_entry(entry_t& entry) { - transactions_deque initial_xacts(entry->transactions.begin(), - entry->transactions.end()); + transactions_deque initial_xacts(entry.transactions.begin(), + entry.transactions.end()); for (transactions_deque::iterator i = initial_xacts.begin(); i != initial_xacts.end(); @@ -23,13 +23,13 @@ void automated_transaction_t::extend_entry(entry_t * entry) transaction_t * xact = new transaction_t((*t)->account, amt, (*t)->flags | TRANSACTION_AUTO); - entry->add_transaction(xact); + entry.add_transaction(xact); } } automated_transactions_t * current_auto_xacts = NULL; -bool handle_auto_xacts(entry_t * entry) +bool handle_auto_xacts(entry_t& entry) { if (current_auto_xacts && ! current_auto_xacts->automated_transactions.empty()) @@ -39,3 +39,17 @@ bool handle_auto_xacts(entry_t * entry) } } // namespace ledger + +#ifdef USE_BOOST_PYTHON + +#include <boost/python.hpp> +#include <Python.h> + +using namespace boost::python; +using namespace ledger; + +void export_autoxact() { + def("handle_auto_xacts", handle_auto_xacts); +} + +#endif // USE_BOOST_PYTHON @@ -34,7 +34,7 @@ public: delete *i; } - void extend_entry(entry_t * entry); + void extend_entry(entry_t& entry); }; @@ -63,7 +63,7 @@ public: delete *i; } - void extend_entry(entry_t * entry) { + void extend_entry(entry_t& entry) { for (automated_transactions_deque::iterator i = automated_transactions.begin(); i != automated_transactions.end(); @@ -90,7 +90,7 @@ public: extern automated_transactions_t * current_auto_xacts; -bool handle_auto_xacts(entry_t * entry); +bool handle_auto_xacts(entry_t& entry); } // namespace ledger @@ -120,6 +120,93 @@ bool entry_t::remove_transaction(transaction_t * xact) return true; } +bool finalize_entry(entry_t& entry) +{ + // Scan through and compute the total balance for the entry. This + // is used for auto-calculating the value of entries with no cost, + // and the per-unit price of unpriced commodities. + + value_t balance; + + bool no_amounts = true; + for (transactions_list::const_iterator x = entry.transactions.begin(); + x != entry.transactions.end(); + x++) + if (! ((*x)->flags & TRANSACTION_VIRTUAL) || + ((*x)->flags & TRANSACTION_BALANCE)) { + amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount; + if (*p) { + if (no_amounts) { + balance = *p; + no_amounts = false; + } else { + balance += *p; + } + } + } + + // If it's a null entry, then let the user have their fun + if (no_amounts) + return true; + + // If one transaction of a two-line transaction is of a different + // commodity than the others, and it has no per-unit price, + // determine its price by dividing the unit count into the value of + // the balance. This is done for the last eligible commodity. + + if (balance.type == value_t::BALANCE && + ((balance_t *) balance.data)->amounts.size() == 2) + for (transactions_list::const_iterator x = entry.transactions.begin(); + x != entry.transactions.end(); + x++) { + if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL)) + continue; + + for (amounts_map::const_iterator i + = ((balance_t *) balance.data)->amounts.begin(); + i != ((balance_t *) balance.data)->amounts.end(); + i++) + if ((*i).second.commodity() != (*x)->amount.commodity()) { + assert((*x)->amount); + balance -= (*x)->amount; + assert(! (*x)->cost); + (*x)->cost = new amount_t(- (*i).second); + balance += *(*x)->cost; + break; + } + + break; + } + + // Walk through each of the transactions, fixing up any that we + // can, and performing any on-the-fly calculations. + + bool empty_allowed = true; + + for (transactions_list::const_iterator x = entry.transactions.begin(); + x != entry.transactions.end(); + x++) { + if ((*x)->amount || ((*x)->flags & TRANSACTION_VIRTUAL)) + continue; + + if (! empty_allowed || ! balance || balance.type != value_t::AMOUNT) + return false; + + empty_allowed = false; + + // If one transaction gives no value at all -- and all the + // rest are of the same commodity -- then its value is the + // inverse of the computed value of the others. + + (*x)->amount = *((amount_t *) balance.data); + (*x)->amount.negate(); + + balance = 0U; + } + + return ! balance; +} + bool entry_t::valid() const { if (! date || date == -1) @@ -296,6 +383,11 @@ journal_t::~journal_t() bool journal_t::add_entry(entry_t * entry) { + if (! run_hooks(entry_finalize_hooks, *entry)) { + delete entry; + return false; + } + entries.push_back(entry); for (transactions_list::const_iterator i = entry->transactions.begin(); @@ -439,6 +531,9 @@ entry_t * journal_t::derive_entry(strings_list::iterator i, } done: + if (! run_hooks(entry_finalize_hooks, *added)) + return NULL; + return added.release(); } @@ -590,6 +685,27 @@ account_t * py_find_account_2(journal_t& journal, const std::string& name, return journal.find_account(name, auto_create); } +#if 0 + +void py_add_entry_finalize_hook(journal_t& journal, object x) +{ + add_hook(journal.entry_finalize_hooks, + extract<journal_t::entry_finalize_hook_t>(x)); +} + +void py_remove_entry_finalize_hook(journal_t& journal, object x) +{ + remove_hook(journal.entry_finalize_hooks, + extract<journal_t::entry_finalize_hook_t>(x)); +} + +void py_run_entry_finalize_hooks(journal_t& journal, entry_t& entry) +{ + run_hooks(journal.entry_finalize_hooks, entry); +} + +#endif + #define EXC_TRANSLATOR(type) \ void exc_translate_ ## type(const type& err) { \ PyErr_SetString(PyExc_RuntimeError, err.what()); \ @@ -677,6 +793,11 @@ void export_journal() .def("add_entry", &journal_t::add_entry) .def("remove_entry", &journal_t::remove_entry) +#if 0 + .def("add_entry_finalize_hook", py_add_entry_finalize_hook) + .def("remove_entry_finalize_hook", py_remove_entry_finalize_hook) + .def("run_entry_finalize_hooks", py_run_entry_finalize_hooks) +#endif .def("derive_entry", &journal_t::derive_entry, return_value_policy<manage_new_object>()) @@ -100,11 +100,10 @@ (if (time-less-p moment date) (throw 'found t))))))) -(defun ledger-add-entry (entry) +(defun ledger-add-entry (entry-text) (interactive - (list (read-string "Entry: " (format-time-string "%Y/%m/%d ")))) - (let* ((args (mapcar 'shell-quote-argument (split-string entry))) - (date (car args)) + (list (read-string "Entry: " (format-time-string "%Y/%m/")))) + (let* ((date (car (split-string entry-text))) (insert-year t) exit-code) (if (string-match "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)" date) (setq date (encode-time 0 0 0 (string-to-int (match-string 3 date)) @@ -117,7 +116,7 @@ (save-excursion (insert (with-temp-buffer - (setq exit-code (apply 'ledger-run-ledger "entry" args)) + (setq exit-code (ledger-run-ledger "entry" entry-text)) (if (= 0 exit-code) (progn (goto-char (point-min)) @@ -125,8 +124,8 @@ (if insert-year (buffer-string) (buffer-substring 5 (point-max)))) - (concat (if insert-year entry - (substring entry 5)) "\n"))) "\n")))) + (concat (if insert-year entry-text + (substring entry-text 5)) "\n"))) "\n")))) (defun ledger-expand-entry () (interactive) @@ -303,8 +302,11 @@ (defun ledger-run-ledger (&rest args) "run ledger with supplied arguments" - (apply 'call-process ledger-binary-path nil t nil - (append (list "-f" ledger-data-file) args))) + (let ((command (mapconcat 'identity + (append (list ledger-binary-path + "-f" ledger-data-file) args) " "))) + (insert (shell-command-to-string command))) + 0) (provide 'ledger) @@ -116,6 +116,8 @@ class entry_t bool valid() const; }; +bool finalize_entry(entry_t& entry); + typedef std::map<const std::string, account_t *> accounts_map; typedef std::pair<const std::string, account_t *> accounts_pair; @@ -181,6 +183,30 @@ class account_t std::ostream& operator<<(std::ostream& out, const account_t& account); +template <typename T> +void add_hook(std::list<T>& list, T func, const bool prepend = false) { + if (prepend) + list.push_front(func); + else + list.push_back(func); +} + +template <typename T> +void remove_hook(std::list<T>& list, T func) { + list.remove(func); +} + +template <typename T, typename Data> +bool run_hooks(std::list<T>& list, Data& entry) { + for (typename std::list<T>::const_iterator i = list.begin(); + i != list.end(); + i++) + if (! (*i)(entry)) + return false; + return true; +} + + typedef std::list<entry_t *> entries_list; typedef std::list<std::string> strings_list; @@ -195,9 +221,14 @@ class journal_t mutable accounts_map accounts_cache; + typedef bool (*entry_finalize_hook_t)(entry_t& entry); + + std::list<entry_finalize_hook_t> entry_finalize_hooks; + journal_t() { master = new account_t(NULL, ""); item_pool = item_pool_end = NULL; + add_hook(entry_finalize_hooks, finalize_entry); } ~journal_t(); @@ -164,6 +164,8 @@ int parse_and_report(int argc, char * argv[], char * envp[]) { std::auto_ptr<journal_t> journal(new journal_t); + add_hook(journal->entry_finalize_hooks, handle_auto_xacts); + // Parse command-line arguments, and those set in the environment TIMER_START(process_opts); @@ -224,7 +226,6 @@ int parse_and_report(int argc, char * argv[], char * envp[]) #endif std::auto_ptr<qif_parser_t> qif_parser(new qif_parser_t); std::auto_ptr<textual_parser_t> text_parser(new textual_parser_t); - text_parser->add_finalize_hook(handle_auto_xacts); register_parser(bin_parser.get()); #ifdef READ_GNUCASH @@ -28,6 +28,7 @@ void export_walk(); void export_format(); void export_valexpr(); void export_datetime(); +void export_autoxact(); void initialize_ledger_for_python() { @@ -48,6 +49,7 @@ void initialize_ledger_for_python() export_format(); export_valexpr(); export_datetime(); + export_autoxact(); module_initialized = true; } @@ -145,93 +145,6 @@ void parse_automated_transactions(std::istream& in, add_automated_transaction(new automated_transaction_t(line + 1, xacts)); } -bool finalize_entry(entry_t * entry) -{ - // Scan through and compute the total balance for the entry. This - // is used for auto-calculating the value of entries with no cost, - // and the per-unit price of unpriced commodities. - - value_t balance; - - bool no_amounts = true; - for (transactions_list::const_iterator x = entry->transactions.begin(); - x != entry->transactions.end(); - x++) - if (! ((*x)->flags & TRANSACTION_VIRTUAL) || - ((*x)->flags & TRANSACTION_BALANCE)) { - amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount; - if (*p) { - if (no_amounts) { - balance = *p; - no_amounts = false; - } else { - balance += *p; - } - } - } - - // If it's a null entry, then let the user have their fun - if (no_amounts) - return true; - - // If one transaction of a two-line transaction is of a different - // commodity than the others, and it has no per-unit price, - // determine its price by dividing the unit count into the value of - // the balance. This is done for the last eligible commodity. - - if (balance.type == value_t::BALANCE && - ((balance_t *) balance.data)->amounts.size() == 2) - for (transactions_list::const_iterator x = entry->transactions.begin(); - x != entry->transactions.end(); - x++) { - if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL)) - continue; - - for (amounts_map::const_iterator i - = ((balance_t *) balance.data)->amounts.begin(); - i != ((balance_t *) balance.data)->amounts.end(); - i++) - if ((*i).second.commodity() != (*x)->amount.commodity()) { - assert((*x)->amount); - balance -= (*x)->amount; - assert(! (*x)->cost); - (*x)->cost = new amount_t(- (*i).second); - balance += *(*x)->cost; - break; - } - - break; - } - - // Walk through each of the transactions, fixing up any that we - // can, and performing any on-the-fly calculations. - - bool empty_allowed = true; - - for (transactions_list::const_iterator x = entry->transactions.begin(); - x != entry->transactions.end(); - x++) { - if ((*x)->amount || ((*x)->flags & TRANSACTION_VIRTUAL)) - continue; - - if (! empty_allowed || ! balance || balance.type != value_t::AMOUNT) - return false; - - empty_allowed = false; - - // If one transaction gives no value at all -- and all the - // rest are of the same commodity -- then its value is the - // inverse of the computed value of the others. - - (*x)->amount = *((amount_t *) balance.data); - (*x)->amount.negate(); - - balance = 0U; - } - - return ! balance; -} - namespace { TIMER_DEF(entry_finish, "finalizing entry"); TIMER_DEF(entry_xacts, "parsing transactions"); @@ -294,17 +207,6 @@ entry_t * parse_entry(std::istream& in, account_t * master, TIMER_STOP(entry_xacts); - // If there were no transactions, throw away the entry - - TIMER_START(entry_finish); - - if (curr->transactions.empty() || - ! parser.run_finalize_hooks(curr.get())) { - return NULL; // ~auto_ptr will delete curr - } - - TIMER_STOP(entry_finish); - return curr.release(); } @@ -423,10 +325,10 @@ unsigned int textual_parser_t::parse(std::istream& in, = new transaction_t(last_account, amt, TRANSACTION_VIRTUAL); curr->add_transaction(xact); - if (! run_finalize_hooks(curr.get()) || - ! journal->add_entry(curr.release())) + if (! journal->add_entry(curr.release())) throw parse_error(path, linenum, "Failed to record 'out' timelog entry"); + count++; } else { throw parse_error(path, linenum, "Cannot parse timelog entry date"); @@ -5,42 +5,13 @@ namespace ledger { -bool finalize_entry(entry_t * entry); - class textual_parser_t : public parser_t { public: - typedef bool (*finalize_hook_t)(entry_t * entry); - - std::list<finalize_hook_t> finalize_hooks; - - textual_parser_t() { - add_finalize_hook(finalize_entry); - } - virtual bool test(std::istream& in) const { return true; } - void add_finalize_hook(finalize_hook_t func, bool prepend = false) { - if (prepend) - finalize_hooks.push_front(func); - else - finalize_hooks.push_back(func); - } - void remove_finalize_hook(finalize_hook_t func) { - finalize_hooks.remove(func); - } - bool run_finalize_hooks(entry_t * entry) { - for (std::list<finalize_hook_t>::const_iterator i - = finalize_hooks.begin(); - i != finalize_hooks.end(); - i++) - if (! (*i)(entry)) - return false; - return true; - } - virtual unsigned int parse(std::istream& in, journal_t * journal, account_t * master = NULL, |