Revised the ways statistics are computed
It is no longer done in calc_posts, but recursively on each account. This allows value expressions to ask statistical questions, like "earliest cleared posting?" (TBD) from any specific account, computed lazily.
This commit is contained in:
parent
4a0f5f9034
commit
f340d50362
8 changed files with 239 additions and 189 deletions
|
|
@ -65,6 +65,7 @@ libledger_data_la_LDFLAGS = -release $(VERSION).0
|
|||
|
||||
libledger_report_la_SOURCES = \
|
||||
src/quotes.cc \
|
||||
src/stats.cc \
|
||||
src/generate.cc \
|
||||
src/derive.cc \
|
||||
src/emacs.cc \
|
||||
|
|
@ -123,6 +124,7 @@ pkginclude_HEADERS = \
|
|||
src/precmd.h \
|
||||
src/derive.h \
|
||||
src/generate.h \
|
||||
src/stats.h \
|
||||
src/output.h \
|
||||
src/emacs.h \
|
||||
src/quotes.h \
|
||||
|
|
|
|||
|
|
@ -309,7 +309,36 @@ std::size_t account_t::children_with_flags(xdata_t::flags_t flags) const
|
|||
account_t::xdata_t::details_t&
|
||||
account_t::xdata_t::details_t::operator+=(const details_t& other)
|
||||
{
|
||||
// jww (2009-03-05): NYI
|
||||
posts_count += other.posts_count;
|
||||
posts_virtuals_count += other.posts_virtuals_count;
|
||||
posts_cleared_count += other.posts_cleared_count;
|
||||
posts_last_7_count += other.posts_last_7_count;
|
||||
posts_last_30_count += other.posts_last_30_count;
|
||||
posts_this_month_count += other.posts_this_month_count;
|
||||
|
||||
if (! is_valid(earliest_post) ||
|
||||
(is_valid(other.earliest_post) &&
|
||||
other.earliest_post < earliest_post))
|
||||
earliest_post = other.earliest_post;
|
||||
if (! is_valid(earliest_cleared_post) ||
|
||||
(is_valid(other.earliest_cleared_post) &&
|
||||
other.earliest_cleared_post < earliest_cleared_post))
|
||||
earliest_cleared_post = other.earliest_cleared_post;
|
||||
|
||||
if (! is_valid(latest_post) ||
|
||||
(is_valid(other.latest_post) &&
|
||||
other.latest_post > latest_post))
|
||||
latest_post = other.latest_post;
|
||||
if (! is_valid(latest_cleared_post) ||
|
||||
(is_valid(other.latest_cleared_post) &&
|
||||
other.latest_cleared_post > latest_cleared_post))
|
||||
latest_cleared_post = other.latest_cleared_post;
|
||||
|
||||
filenames.insert(other.filenames.begin(), other.filenames.end());
|
||||
accounts_referenced.insert(other.accounts_referenced.begin(),
|
||||
other.accounts_referenced.end());
|
||||
payees_referenced.insert(other.payees_referenced.begin(),
|
||||
other.payees_referenced.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +406,7 @@ account_t::family_details(bool gather_all) const
|
|||
foreach (const accounts_map::value_type& pair, accounts)
|
||||
xdata_->family_details += pair.second->family_details(gather_all);
|
||||
|
||||
xdata_->self_details += self_details(gather_all);
|
||||
xdata_->family_details += self_details(gather_all);
|
||||
}
|
||||
return xdata_->family_details;
|
||||
}
|
||||
|
|
@ -385,16 +414,8 @@ account_t::family_details(bool gather_all) const
|
|||
void account_t::xdata_t::details_t::update(post_t& post,
|
||||
bool gather_all)
|
||||
{
|
||||
if (last_xact != post.xact) {
|
||||
xacts_count++;
|
||||
last_xact = post.xact;
|
||||
}
|
||||
if (last_post == &post)
|
||||
return;
|
||||
|
||||
last_post = &post;
|
||||
|
||||
posts_count++;
|
||||
|
||||
if (post.has_flags(POST_VIRTUAL))
|
||||
posts_virtuals_count++;
|
||||
|
||||
|
|
|
|||
|
|
@ -143,8 +143,6 @@ class account_t : public scope_t
|
|||
bool gathered;
|
||||
|
||||
// The following are only calculated if --totals is enabled
|
||||
std::size_t xacts_count;
|
||||
|
||||
std::size_t posts_count;
|
||||
std::size_t posts_virtuals_count;
|
||||
std::size_t posts_cleared_count;
|
||||
|
|
@ -157,9 +155,6 @@ class account_t : public scope_t
|
|||
date_t latest_post;
|
||||
date_t latest_cleared_post;
|
||||
|
||||
xact_t * last_xact;
|
||||
post_t * last_post;
|
||||
|
||||
std::size_t last_size;
|
||||
|
||||
// The following are only calculated if --gather is enabled
|
||||
|
|
@ -171,8 +166,6 @@ class account_t : public scope_t
|
|||
: calculated(false),
|
||||
gathered(false),
|
||||
|
||||
xacts_count(0),
|
||||
|
||||
posts_count(0),
|
||||
posts_virtuals_count(0),
|
||||
posts_cleared_count(0),
|
||||
|
|
@ -180,14 +173,6 @@ class account_t : public scope_t
|
|||
posts_last_30_count(0),
|
||||
posts_this_month_count(0),
|
||||
|
||||
earliest_post(0),
|
||||
earliest_cleared_post(0),
|
||||
latest_post(0),
|
||||
latest_cleared_post(0),
|
||||
|
||||
last_xact(NULL),
|
||||
last_post(NULL),
|
||||
|
||||
last_size(0) {}
|
||||
|
||||
details_t& operator+=(const details_t& other);
|
||||
|
|
|
|||
|
|
@ -111,104 +111,6 @@ void format_posts::operator()(post_t& post)
|
|||
}
|
||||
}
|
||||
|
||||
void gather_statistics::flush()
|
||||
{
|
||||
std::ostream& out(report.output_stream);
|
||||
|
||||
{
|
||||
straccstream accum;
|
||||
out << ACCUM(accum << "Time period: %1 to %2" << statistics.earliest_post
|
||||
<< statistics.latest_post) << std::endl << std::endl;
|
||||
}
|
||||
|
||||
out << _(" Files these postings came from:") << std::endl;
|
||||
|
||||
foreach (const path& pathname, statistics.filenames)
|
||||
if (! pathname.empty())
|
||||
out << " " << pathname.string() << std::endl;
|
||||
out << std::endl;
|
||||
|
||||
out << _(" Unique payees: ");
|
||||
out.width(8);
|
||||
out << std::right << statistics.payees_referenced.size() << std::endl;
|
||||
|
||||
out << _(" Unique accounts: ");
|
||||
out.width(8);
|
||||
out << std::right << statistics.accounts_referenced.size() << std::endl;
|
||||
|
||||
out << _(" Number of transactions: ") ;
|
||||
out.width(8);
|
||||
out << std::right << statistics.total_xacts << std::endl;
|
||||
|
||||
out << _(" Number of postings: ");
|
||||
out.width(8);
|
||||
out << std::right << statistics.total_posts;
|
||||
|
||||
out << " (";
|
||||
out.precision(2);
|
||||
out << (double((statistics.latest_post - statistics.earliest_post).days()) /
|
||||
double(statistics.total_posts)) << _(" per day)") << std::endl;
|
||||
|
||||
out << _(" Days since last post: ");
|
||||
out.width(8);
|
||||
out << std::right << (CURRENT_DATE() - statistics.latest_post).days()
|
||||
<< std::endl;
|
||||
|
||||
out << _(" Posts in last 7 days: ");
|
||||
out.width(8);
|
||||
out << std::right << statistics.total_last_7_days << std::endl;
|
||||
out << _(" Posts in last 30 days: ");
|
||||
out.width(8);
|
||||
out << std::right << statistics.total_last_30_days << std::endl;
|
||||
out << _(" Posts seen this month: ");
|
||||
out.width(8);
|
||||
out << std::right << statistics.total_this_month << std::endl;
|
||||
|
||||
out << _(" Uncleared postings: ");
|
||||
out.width(8);
|
||||
out << std::right << statistics.total_uncleared_posts << std::endl;
|
||||
|
||||
out.flush();
|
||||
}
|
||||
|
||||
void gather_statistics::operator()(post_t& post)
|
||||
{
|
||||
if (last_xact != post.xact) {
|
||||
statistics.total_xacts++;
|
||||
last_xact = post.xact;
|
||||
}
|
||||
if (last_post != &post) {
|
||||
statistics.total_posts++;
|
||||
last_post = &post;
|
||||
|
||||
statistics.filenames.insert(post.pathname);
|
||||
|
||||
date_t date = post.date();
|
||||
|
||||
if (date.year() == CURRENT_DATE().year() &&
|
||||
date.month() == CURRENT_DATE().month())
|
||||
statistics.total_this_month++;
|
||||
|
||||
if ((CURRENT_DATE() - date).days() <= 30)
|
||||
statistics.total_last_30_days++;
|
||||
if ((CURRENT_DATE() - date).days() <= 7)
|
||||
statistics.total_last_7_days++;
|
||||
|
||||
if (post.state() != item_t::CLEARED)
|
||||
statistics.total_uncleared_posts++;
|
||||
|
||||
if (! is_valid(statistics.earliest_post) ||
|
||||
post.date() < statistics.earliest_post)
|
||||
statistics.earliest_post = post.date();
|
||||
if (! is_valid(statistics.latest_post) ||
|
||||
post.date() > statistics.latest_post)
|
||||
statistics.latest_post = post.date();
|
||||
|
||||
statistics.accounts_referenced.insert(post.account->fullname());
|
||||
statistics.payees_referenced.insert(post.xact->payee);
|
||||
}
|
||||
}
|
||||
|
||||
format_accounts::format_accounts(report_t& _report,
|
||||
const string& format)
|
||||
: report(_report), disp_pred()
|
||||
|
|
|
|||
46
src/output.h
46
src/output.h
|
|
@ -84,52 +84,6 @@ public:
|
|||
virtual void operator()(post_t& post);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
class gather_statistics : public item_handler<post_t>
|
||||
{
|
||||
protected:
|
||||
report_t& report;
|
||||
xact_t * last_xact;
|
||||
post_t * last_post;
|
||||
|
||||
struct statistics_t {
|
||||
std::set<path> filenames;
|
||||
|
||||
std::size_t total_xacts;
|
||||
std::size_t total_posts;
|
||||
std::size_t total_uncleared_posts;
|
||||
std::size_t total_last_7_days;
|
||||
std::size_t total_last_30_days;
|
||||
std::size_t total_this_month;
|
||||
|
||||
date_t earliest_post;
|
||||
date_t latest_post;
|
||||
|
||||
std::set<string> accounts_referenced;
|
||||
std::set<string> payees_referenced;
|
||||
|
||||
statistics_t()
|
||||
: total_xacts(0), total_posts(0), total_uncleared_posts(0),
|
||||
total_last_7_days(0), total_last_30_days(0), total_this_month(0) {}
|
||||
} statistics;
|
||||
|
||||
public:
|
||||
gather_statistics(report_t& _report)
|
||||
: report(_report), last_xact(NULL), last_post(NULL) {
|
||||
TRACE_CTOR(gather_statistics, "report&");
|
||||
}
|
||||
virtual ~gather_statistics() {
|
||||
TRACE_DTOR(gather_statistics);
|
||||
}
|
||||
|
||||
virtual void flush();
|
||||
virtual void operator()(post_t& post);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Brief
|
||||
*
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "iterators.h"
|
||||
#include "filters.h"
|
||||
#include "precmd.h"
|
||||
#include "stats.h"
|
||||
#include "generate.h"
|
||||
#include "derive.h"
|
||||
#include "emacs.h"
|
||||
|
|
@ -728,8 +729,9 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
|
|||
|
||||
case 's':
|
||||
if (is_eq(q, "stats") || is_eq(q, "stat"))
|
||||
return WRAP_FUNCTOR(reporter<>(new gather_statistics(*this), *this));
|
||||
else if (is_eq(q, "server") && maybe_import("ledger.server"))
|
||||
return WRAP_FUNCTOR(report_statistics);
|
||||
else
|
||||
if (is_eq(q, "server") && maybe_import("ledger.server"))
|
||||
return session.lookup(string(CMD_PREFIX) + "server");
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
125
src/stats.cc
Normal file
125
src/stats.cc
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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 "derive.h"
|
||||
#include "xact.h"
|
||||
#include "post.h"
|
||||
#include "account.h"
|
||||
#include "report.h"
|
||||
#include "session.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
value_t report_statistics(call_scope_t& args)
|
||||
{
|
||||
report_t& report(find_scope<report_t>(args));
|
||||
std::ostream& out(report.output_stream);
|
||||
|
||||
const account_t::xdata_t::details_t&
|
||||
statistics(report.session.master->family_details(true));
|
||||
|
||||
if (! is_valid(statistics.earliest_post) &&
|
||||
! is_valid(statistics.latest_post))
|
||||
return NULL_VALUE;
|
||||
|
||||
assert(is_valid(statistics.earliest_post));
|
||||
assert(is_valid(statistics.latest_post));
|
||||
|
||||
{
|
||||
straccstream accum;
|
||||
out << ACCUM(accum << _("Time period: %1 to %2 (%3 days)")
|
||||
<< format_date(statistics.earliest_post)
|
||||
<< format_date(statistics.latest_post)
|
||||
<< (statistics.latest_post -
|
||||
statistics.earliest_post).days())
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
|
||||
out << _(" Files these postings came from:") << std::endl;
|
||||
|
||||
foreach (const path& pathname, statistics.filenames)
|
||||
if (! pathname.empty())
|
||||
out << " " << pathname.string() << std::endl;
|
||||
out << std::endl;
|
||||
|
||||
out << _(" Unique payees: ");
|
||||
out.width(6);
|
||||
out << statistics.payees_referenced.size() << std::endl;
|
||||
|
||||
out << _(" Unique accounts: ");
|
||||
out.width(6);
|
||||
out << statistics.accounts_referenced.size() << std::endl;
|
||||
|
||||
out << std::endl;
|
||||
|
||||
#if 0
|
||||
out << _(" Number of transactions: ") ;
|
||||
out.width(6);
|
||||
out << statistics.xacts_count << std::endl;
|
||||
#endif
|
||||
|
||||
out << _(" Number of postings: ");
|
||||
out.width(6);
|
||||
out << statistics.posts_count;
|
||||
|
||||
out << " (";
|
||||
out.precision(2);
|
||||
out << (double((statistics.latest_post - statistics.earliest_post).days()) /
|
||||
double(statistics.posts_count)) << _(" per day)") << std::endl;
|
||||
|
||||
out << _(" Uncleared postings: ");
|
||||
out.width(6);
|
||||
out << (statistics.posts_count -
|
||||
statistics.posts_cleared_count) << std::endl;
|
||||
|
||||
out << std::endl;
|
||||
|
||||
out << _(" Days since last post: ");
|
||||
out.width(6);
|
||||
out << (CURRENT_DATE() - statistics.latest_post).days()
|
||||
<< std::endl;
|
||||
|
||||
out << _(" Posts in last 7 days: ");
|
||||
out.width(6);
|
||||
out << statistics.posts_last_7_count << std::endl;
|
||||
out << _(" Posts in last 30 days: ");
|
||||
out.width(6);
|
||||
out << statistics.posts_last_30_count << std::endl;
|
||||
out << _(" Posts seen this month: ");
|
||||
out.width(6);
|
||||
out << statistics.posts_this_month_count << std::endl;
|
||||
|
||||
out.flush();
|
||||
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
59
src/stats.h
Normal file
59
src/stats.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup stats
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file stats.h
|
||||
* @author John Wiegley
|
||||
*
|
||||
* @ingroup report
|
||||
*
|
||||
* @brief Brief
|
||||
*
|
||||
* Long.
|
||||
*/
|
||||
#ifndef _STATS_H
|
||||
#define _STATS_H
|
||||
|
||||
#include "value.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
class call_scope_t;
|
||||
|
||||
value_t report_statistics(call_scope_t& scope);
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _STATS_H
|
||||
Loading…
Add table
Reference in a new issue