This lets you run standard report queries against a Ledger journal and
get back a collection of postings, for example:
import ledger
journal = ledger.Journal("sample.dat")
posts = journal.collect("-M assets")
for post in posts:
print post.account
However, this code is not really working yet for a large variety of
reasons, having to do with object life-time, shallow copying, and lack
of reference counting where it should be. For instance, calling
ledger.Journal().collect() fails because the temporary journal object is
destroyed and the collection now has a host of bad pointers. Using "for
post in journal.collect()" fails too with other bad pointers. And the
whole lot of it crashes on exit at the moment.
313 lines
9.4 KiB
C++
313 lines
9.4 KiB
C++
/*
|
|
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* - Neither the name of New Artisans LLC nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <system.hh>
|
|
|
|
#include "pyinterp.h"
|
|
#include "pyutils.h"
|
|
#include "journal.h"
|
|
#include "xact.h"
|
|
#include "post.h"
|
|
#include "chain.h"
|
|
#include "filters.h"
|
|
#include "iterators.h"
|
|
#include "scope.h"
|
|
#include "report.h"
|
|
|
|
namespace ledger {
|
|
|
|
using namespace boost::python;
|
|
|
|
namespace {
|
|
|
|
account_t& py_account_master(journal_t& journal) {
|
|
return *journal.master;
|
|
}
|
|
|
|
commodity_pool_t& py_commodity_pool(journal_t& journal) {
|
|
return *journal.commodity_pool;
|
|
}
|
|
|
|
long xacts_len(journal_t& journal)
|
|
{
|
|
return journal.xacts.size();
|
|
}
|
|
|
|
xact_t& xacts_getitem(journal_t& journal, long i)
|
|
{
|
|
static long last_index = 0;
|
|
static journal_t * last_journal = NULL;
|
|
static xacts_list::iterator elem;
|
|
|
|
long len = journal.xacts.size();
|
|
|
|
if (labs(i) >= len) {
|
|
PyErr_SetString(PyExc_IndexError, _("Index out of range"));
|
|
throw_error_already_set();
|
|
}
|
|
|
|
if (&journal == last_journal && i == last_index + 1) {
|
|
last_index = i;
|
|
return **++elem;
|
|
}
|
|
|
|
long x = i < 0 ? len + i : i;
|
|
elem = journal.xacts.begin();
|
|
while (--x >= 0)
|
|
elem++;
|
|
|
|
last_journal = &journal;
|
|
last_index = i;
|
|
|
|
return **elem;
|
|
}
|
|
|
|
long accounts_len(account_t& account)
|
|
{
|
|
return account.accounts.size();
|
|
}
|
|
|
|
account_t& accounts_getitem(account_t& account, long i)
|
|
{
|
|
static long last_index = 0;
|
|
static account_t * last_account = NULL;
|
|
static accounts_map::iterator elem;
|
|
|
|
long len = account.accounts.size();
|
|
|
|
if (labs(i) >= len) {
|
|
PyErr_SetString(PyExc_IndexError, _("Index out of range"));
|
|
throw_error_already_set();
|
|
}
|
|
|
|
if (&account == last_account && i == last_index + 1) {
|
|
last_index = i;
|
|
return *(*++elem).second;
|
|
}
|
|
|
|
long x = i < 0 ? len + i : i;
|
|
elem = account.accounts.begin();
|
|
while (--x >= 0)
|
|
elem++;
|
|
|
|
last_account = &account;
|
|
last_index = i;
|
|
|
|
return *(*elem).second;
|
|
}
|
|
|
|
account_t * py_find_account_1(journal_t& journal, const string& name)
|
|
{
|
|
return journal.find_account(name);
|
|
}
|
|
|
|
account_t * py_find_account_2(journal_t& journal, const string& name,
|
|
const bool auto_create)
|
|
{
|
|
return journal.find_account(name, auto_create);
|
|
}
|
|
|
|
std::size_t py_read(journal_t& journal, const string& pathname)
|
|
{
|
|
return journal.read(pathname);
|
|
}
|
|
|
|
struct collector_wrapper
|
|
{
|
|
journal_t& journal;
|
|
report_t report;
|
|
collect_posts * posts_collector;
|
|
post_handler_ptr chain;
|
|
|
|
collector_wrapper(journal_t& _journal, report_t& base)
|
|
: journal(_journal), report(base),
|
|
posts_collector(new collect_posts) {}
|
|
~collector_wrapper() {
|
|
journal.clear_xdata();
|
|
}
|
|
|
|
std::size_t length() const {
|
|
return posts_collector->length();
|
|
}
|
|
|
|
std::vector<post_t *>::iterator begin() {
|
|
return posts_collector->begin();
|
|
}
|
|
std::vector<post_t *>::iterator end() {
|
|
return posts_collector->end();
|
|
}
|
|
};
|
|
|
|
shared_ptr<collector_wrapper>
|
|
py_collect(journal_t& journal, const string& query)
|
|
{
|
|
if (journal.has_xdata()) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
_("Cannot have multiple journal collections open at once"));
|
|
throw_error_already_set();
|
|
}
|
|
|
|
report_t& current_report(downcast<report_t>(*scope_t::default_scope));
|
|
shared_ptr<collector_wrapper> coll(new collector_wrapper(journal,
|
|
current_report));
|
|
std::auto_ptr<journal_t> save_journal
|
|
(current_report.session.journal.release());
|
|
current_report.session.journal.reset(&journal);
|
|
|
|
try {
|
|
strings_list remaining =
|
|
process_arguments(split_arguments(query.c_str()), coll->report);
|
|
coll->report.normalize_options("register");
|
|
|
|
value_t args;
|
|
foreach (const string& arg, remaining)
|
|
args.push_back(string_value(arg));
|
|
coll->report.parse_query_args(args, "@Journal.collect");
|
|
|
|
journal_posts_iterator walker(coll->journal);
|
|
coll->chain =
|
|
chain_post_handlers(coll->report,
|
|
post_handler_ptr(coll->posts_collector));
|
|
pass_down_posts(coll->chain, walker);
|
|
}
|
|
catch (...) {
|
|
current_report.session.journal.release();
|
|
current_report.session.journal.reset(save_journal.release());
|
|
throw;
|
|
}
|
|
current_report.session.journal.release();
|
|
current_report.session.journal.reset(save_journal.release());
|
|
|
|
return coll;
|
|
}
|
|
|
|
post_t * posts_getitem(collector_wrapper& collector, long i)
|
|
{
|
|
post_t * post = collector.posts_collector->posts[i];
|
|
std::cerr << typeid(post).name() << std::endl;
|
|
std::cerr << typeid(*post).name() << std::endl;
|
|
std::cerr << typeid(post->account).name() << std::endl;
|
|
std::cerr << typeid(*post->account).name() << std::endl;
|
|
return post;
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
void export_journal()
|
|
{
|
|
class_< item_handler<post_t>, shared_ptr<item_handler<post_t> >,
|
|
boost::noncopyable >("PostHandler")
|
|
;
|
|
|
|
class_< collect_posts, bases<item_handler<post_t> >,
|
|
shared_ptr<collect_posts>, boost::noncopyable >("PostCollector")
|
|
.def("__len__", &collect_posts::length)
|
|
.def("__iter__", range<return_internal_reference<> >
|
|
(&collect_posts::begin, &collect_posts::end))
|
|
;
|
|
|
|
class_< collector_wrapper, shared_ptr<collector_wrapper>,
|
|
boost::noncopyable >("PostCollectorWrapper", no_init)
|
|
.def("__len__", &collector_wrapper::length)
|
|
.def("__getitem__", posts_getitem, return_internal_reference<>())
|
|
.def("__iter__", range<return_internal_reference<> >
|
|
(&collector_wrapper::begin, &collector_wrapper::end))
|
|
;
|
|
|
|
class_< journal_t::fileinfo_t > ("FileInfo")
|
|
.def(init<path>())
|
|
|
|
.add_property("filename",
|
|
make_getter(&journal_t::fileinfo_t::filename),
|
|
make_setter(&journal_t::fileinfo_t::filename))
|
|
.add_property("size",
|
|
make_getter(&journal_t::fileinfo_t::size),
|
|
make_setter(&journal_t::fileinfo_t::size))
|
|
.add_property("modtime",
|
|
make_getter(&journal_t::fileinfo_t::modtime),
|
|
make_setter(&journal_t::fileinfo_t::modtime))
|
|
.add_property("from_stream",
|
|
make_getter(&journal_t::fileinfo_t::from_stream),
|
|
make_setter(&journal_t::fileinfo_t::from_stream))
|
|
;
|
|
|
|
class_< journal_t, boost::noncopyable > ("Journal")
|
|
.def(init<path>())
|
|
.def(init<string>())
|
|
|
|
.add_property("master", make_getter(&journal_t::master,
|
|
return_internal_reference<>()))
|
|
.add_property("bucket",
|
|
make_getter(&journal_t::bucket,
|
|
return_internal_reference<>()),
|
|
make_setter(&journal_t::bucket))
|
|
.add_property("was_loaded", make_getter(&journal_t::was_loaded))
|
|
.add_property("commodity_pool",
|
|
make_getter(&journal_t::commodity_pool,
|
|
return_internal_reference<>()))
|
|
|
|
.def("add_account", &journal_t::add_account)
|
|
.def("remove_account", &journal_t::remove_account)
|
|
|
|
.def("find_account", py_find_account_1, return_internal_reference<>())
|
|
.def("find_account", py_find_account_2, return_internal_reference<>())
|
|
.def("find_account_re", &journal_t::find_account_re,
|
|
return_internal_reference<>())
|
|
|
|
.def("add_xact", &journal_t::add_xact)
|
|
.def("remove_xact", &journal_t::remove_xact)
|
|
|
|
.def("__len__", xacts_len)
|
|
.def("__getitem__", xacts_getitem, return_internal_reference<>())
|
|
|
|
.def("__iter__", range<return_internal_reference<> >
|
|
(&journal_t::xacts_begin, &journal_t::xacts_end))
|
|
.def("xacts", range<return_internal_reference<> >
|
|
(&journal_t::xacts_begin, &journal_t::xacts_end))
|
|
.def("auto_xacts", range<return_internal_reference<> >
|
|
(&journal_t::auto_xacts_begin, &journal_t::auto_xacts_end))
|
|
.def("period_xacts", range<return_internal_reference<> >
|
|
(&journal_t::period_xacts_begin, &journal_t::period_xacts_end))
|
|
.def("sources", range<return_internal_reference<> >
|
|
(&journal_t::sources_begin, &journal_t::sources_end))
|
|
|
|
.def("read", py_read)
|
|
|
|
.def("has_xdata", &journal_t::has_xdata)
|
|
.def("clear_xdata", &journal_t::clear_xdata)
|
|
|
|
.def("collect", py_collect)
|
|
|
|
.def("valid", &journal_t::valid)
|
|
;
|
|
}
|
|
|
|
} // namespace ledger
|