Added new --descend option.

This commit is contained in:
John Wiegley 2006-03-12 03:06:11 +00:00
parent 49ae3b65d5
commit 4f83a2bf8f
6 changed files with 190 additions and 29 deletions

View file

@ -76,6 +76,8 @@ void config_t::reset()
secondary_predicate = ""; secondary_predicate = "";
display_predicate = ""; display_predicate = "";
descend_expr = "";
head_entries = 0; head_entries = 0;
tail_entries = 0; tail_entries = 0;
@ -322,6 +324,8 @@ config_t::chain_xact_handlers(const std::string& command,
account_t * master, account_t * master,
std::list<item_handler<transaction_t> *>& ptrs) std::list<item_handler<transaction_t> *>& ptrs)
{ {
bool remember_components = false;
item_handler<transaction_t> * formatter = NULL; item_handler<transaction_t> * formatter = NULL;
ptrs.push_back(formatter = base_formatter); ptrs.push_back(formatter = base_formatter);
@ -348,6 +352,30 @@ config_t::chain_xact_handlers(const std::string& command,
// transactions are included or excluded from the running total. // transactions are included or excluded from the running total.
ptrs.push_back(formatter = new calc_transactions(formatter)); ptrs.push_back(formatter = new calc_transactions(formatter));
// component_transactions looks for reported transaction that
// match the given `descend_expr', and then reports the
// transactions which made up the total for that reported
// transaction.
if (! descend_expr.empty()) {
std::list<std::string> descend_exprs;
std::string::size_type beg = 0;
for (std::string::size_type pos = descend_expr.find(';');
pos != std::string::npos;
beg = pos + 1, pos = descend_expr.find(';', beg))
descend_exprs.push_back(std::string(descend_expr, beg, pos));
descend_exprs.push_back(std::string(descend_expr, beg));
for (std::list<std::string>::reverse_iterator i =
descend_exprs.rbegin();
i != descend_exprs.rend();
i++)
ptrs.push_back(formatter =
new component_transactions(formatter, *i));
remember_components = true;
}
// reconcile_transactions will pass through only those // reconcile_transactions will pass through only those
// transactions which can be reconciled to a given balance // transactions which can be reconciled to a given balance
// (calculated against the transactions which it receives). // (calculated against the transactions which it receives).
@ -400,18 +428,22 @@ config_t::chain_xact_handlers(const std::string& command,
// reports all the transactions that fall on each subsequent day // reports all the transactions that fall on each subsequent day
// of the week. // of the week.
if (show_subtotal && ! (command == "b" || command == "E")) if (show_subtotal && ! (command == "b" || command == "E"))
ptrs.push_back(formatter = new subtotal_transactions(formatter)); ptrs.push_back(formatter =
new subtotal_transactions(formatter, remember_components));
if (days_of_the_week) if (days_of_the_week)
ptrs.push_back(formatter = new dow_transactions(formatter)); ptrs.push_back(formatter =
new dow_transactions(formatter, remember_components));
else if (by_payee) else if (by_payee)
ptrs.push_back(formatter = new by_payee_transactions(formatter)); ptrs.push_back(formatter =
new by_payee_transactions(formatter, remember_components));
if (! report_period.empty()) { if (! report_period.empty()) {
ptrs.push_back(formatter = ptrs.push_back(formatter =
new interval_transactions(formatter, new interval_transactions(formatter,
report_period, report_period,
report_period_sort)); report_period_sort,
remember_components));
ptrs.push_back(formatter = new sort_transactions(formatter, "d")); ptrs.push_back(formatter = new sort_transactions(formatter, "d"));
} }
@ -927,6 +959,23 @@ OPT_BEGIN(related, "r") {
config->show_related = true; config->show_related = true;
} OPT_END(related); } OPT_END(related);
OPT_BEGIN(descend, "") {
std::string arg(optarg);
std::string::size_type beg = 0;
config->descend_expr = "";
for (std::string::size_type pos = arg.find(';');
pos != std::string::npos;
beg = pos + 1, pos = arg.find(';', beg))
config->descend_expr += (std::string("t=={") +
std::string(arg, beg, pos) + "};");
config->descend_expr += (std::string("t=={") +
std::string(arg, beg) + "}");
} OPT_END(descend);
OPT_BEGIN(descend_if, "") {
config->descend_expr = optarg;
} OPT_END(descend_if);
OPT_BEGIN(period, "p:") { OPT_BEGIN(period, "p:") {
if (config->report_period.empty()) { if (config->report_period.empty()) {
config->report_period = optarg; config->report_period = optarg;
@ -1157,6 +1206,8 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "current", 'c', false, opt_current, false }, { "current", 'c', false, opt_current, false },
{ "date-format", 'y', true, opt_date_format, false }, { "date-format", 'y', true, opt_date_format, false },
{ "debug", '\0', true, opt_debug, false }, { "debug", '\0', true, opt_debug, false },
{ "descend", '\0', true, opt_descend, false },
{ "descend-if", '\0', true, opt_descend_if, false },
{ "deviation", 'D', false, opt_deviation, false }, { "deviation", 'D', false, opt_deviation, false },
{ "display", 'd', true, opt_display, false }, { "display", 'd', true, opt_display, false },
{ "dow", '\0', false, opt_dow, false }, { "dow", '\0', false, opt_dow, false },

View file

@ -44,6 +44,7 @@ class config_t
std::string sort_string; std::string sort_string;
std::string amount_expr; std::string amount_expr;
std::string total_expr; std::string total_expr;
std::string descend_expr;
std::string forecast_limit; std::string forecast_limit;
std::string reconcile_balance; std::string reconcile_balance;
std::string reconcile_date; std::string reconcile_date;
@ -106,7 +107,7 @@ class config_t
std::list<item_handler<transaction_t> *>& ptrs); std::list<item_handler<transaction_t> *>& ptrs);
}; };
#define CONFIG_OPTIONS_SIZE 84 #define CONFIG_OPTIONS_SIZE 86
extern option_t config_options[CONFIG_OPTIONS_SIZE]; extern option_t config_options[CONFIG_OPTIONS_SIZE];
void option_help(std::ostream& out); void option_help(std::ostream& out);

View file

@ -1578,6 +1578,17 @@ bool write_value_expr(std::ostream& out,
case value_expr_t::COST_TOTAL: out << "total_cost"; break; case value_expr_t::COST_TOTAL: out << "total_cost"; break;
case value_expr_t::F_NOW: out << "now"; break; case value_expr_t::F_NOW: out << "now"; break;
case value_expr_t::VALUE_EXPR:
if (write_value_expr(out, amount_expr->parsed,
node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::TOTAL_EXPR:
if (write_value_expr(out, total_expr->parsed,
node_to_find, start_pos, end_pos))
found = true;
break;
case value_expr_t::F_ARITH_MEAN: case value_expr_t::F_ARITH_MEAN:
out << "average("; out << "average(";
if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos)) if (write_value_expr(out, node->left, node_to_find, start_pos, end_pos))
@ -1883,6 +1894,9 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
case value_expr_t::PRICE_TOTAL: out << "PRICE_TOTAL"; break; case value_expr_t::PRICE_TOTAL: out << "PRICE_TOTAL"; break;
case value_expr_t::COST_TOTAL: out << "COST_TOTAL"; break; case value_expr_t::COST_TOTAL: out << "COST_TOTAL"; break;
case value_expr_t::VALUE_EXPR: out << "VALUE_EXPR"; break;
case value_expr_t::TOTAL_EXPR: out << "TOTAL_EXPR"; break;
case value_expr_t::F_NOW: out << "F_NOW"; break; case value_expr_t::F_NOW: out << "F_NOW"; break;
case value_expr_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break; case value_expr_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break;
case value_expr_t::F_ABS: out << "F_ABS"; break; case value_expr_t::F_ABS: out << "F_ABS"; break;

View file

@ -427,6 +427,12 @@ public:
guarded_compute(parsed, temp, details, context); guarded_compute(parsed, temp, details, context);
return temp; return temp;
} }
friend bool write_value_expr(std::ostream& out,
const value_expr_t * node,
const value_expr_t * node_to_find,
unsigned long * start_pos,
unsigned long * end_pos);
}; };
extern std::auto_ptr<value_expr> amount_expr; extern std::auto_ptr<value_expr> amount_expr;

38
walk.cc
View file

@ -181,7 +181,8 @@ void handle_value(const value_t& value,
unsigned int flags, unsigned int flags,
std::list<transaction_t>& temps, std::list<transaction_t>& temps,
item_handler<transaction_t>& handler, item_handler<transaction_t>& handler,
const std::time_t date = 0) const std::time_t date = 0,
transactions_list * component_xacts = NULL)
{ {
temps.push_back(transaction_t(account)); temps.push_back(transaction_t(account));
transaction_t& xact(temps.back()); transaction_t& xact(temps.back());
@ -189,6 +190,12 @@ void handle_value(const value_t& value,
xact.flags |= TRANSACTION_BULK_ALLOC; xact.flags |= TRANSACTION_BULK_ALLOC;
entry->add_transaction(&xact); entry->add_transaction(&xact);
// If there are component transactions to associate with this
// temporary, do so now.
if (component_xacts)
transaction_xdata(xact).copy_component_xacts(*component_xacts);
// If the account for this transaction is all virtual, then report // If the account for this transaction is all virtual, then report
// the transaction as such. This allows subtotal reports to show // the transaction as such. This allows subtotal reports to show
// "(Account)" for accounts that contain only virtual transactions. // "(Account)" for accounts that contain only virtual transactions.
@ -343,6 +350,17 @@ void changed_value_transactions::operator()(transaction_t& xact)
last_xact = &xact; last_xact = &xact;
} }
void component_transactions::operator()(transaction_t& xact)
{
if (handler && pred(xact)) {
if (transaction_has_xdata(xact) &&
transaction_xdata_(xact).have_component_xacts())
transaction_xdata_(xact).walk_component_xacts(*handler);
else
(*handler)(xact);
}
}
void subtotal_transactions::report_subtotal(const char * spec_fmt) void subtotal_transactions::report_subtotal(const char * spec_fmt)
{ {
char buf[256]; char buf[256];
@ -364,7 +382,7 @@ void subtotal_transactions::report_subtotal(const char * spec_fmt)
i != values.end(); i != values.end();
i++) i++)
handle_value((*i).second.value, (*i).second.account, &entry, 0, handle_value((*i).second.value, (*i).second.account, &entry, 0,
xact_temps, *handler, finish); xact_temps, *handler, finish, &(*i).second.components);
values.clear(); values.clear();
} }
@ -383,9 +401,18 @@ void subtotal_transactions::operator()(transaction_t& xact)
if (i == values.end()) { if (i == values.end()) {
value_t temp; value_t temp;
add_transaction_to(xact, temp); add_transaction_to(xact, temp);
values.insert(values_pair(acct->fullname(), acct_value_t(acct, temp))); std::pair<values_map::iterator, bool> result
= values.insert(values_pair(acct->fullname(),
acct_value_t(acct, temp)));
assert(result.second);
if (remember_components)
(*result.first).second.components.push_back(&xact);
} else { } else {
add_transaction_to(xact, (*i).second.value); add_transaction_to(xact, (*i).second.value);
if (remember_components)
(*i).second.components.push_back(&xact);
} }
// If the account for this transaction is all virtual, mark it as // If the account for this transaction is all virtual, mark it as
@ -478,8 +505,9 @@ void by_payee_transactions::operator()(transaction_t& xact)
{ {
payee_subtotals_map::iterator i = payee_subtotals.find(xact.entry->payee); payee_subtotals_map::iterator i = payee_subtotals.find(xact.entry->payee);
if (i == payee_subtotals.end()) { if (i == payee_subtotals.end()) {
payee_subtotals_pair temp(xact.entry->payee, payee_subtotals_pair
new subtotal_transactions(handler)); temp(xact.entry->payee,
new subtotal_transactions(handler, remember_components));
std::pair<payee_subtotals_map::iterator, bool> result std::pair<payee_subtotals_map::iterator, bool> result
= payee_subtotals.insert(temp); = payee_subtotals.insert(temp);

99
walk.h
View file

@ -94,8 +94,43 @@ struct transaction_xdata_t
account_t * account; account_t * account;
void * ptr; void * ptr;
transactions_list * component_xacts;
transaction_xdata_t() transaction_xdata_t()
: index(0), dflags(0), date(0), account(0), ptr(0) {} : index(0), dflags(0), date(0), account(NULL), ptr(NULL),
component_xacts(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_xdata_t " << this);
}
~transaction_xdata_t() {
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_xdata_t " << this);
if (component_xacts)
delete component_xacts;
}
void remember_xact(transaction_t& xact) {
if (! component_xacts)
component_xacts = new transactions_list;
component_xacts->push_back(&xact);
}
bool have_component_xacts() const {
return component_xacts != NULL && ! component_xacts->empty();
}
void copy_component_xacts(transactions_list& xacts) {
for (transactions_list::const_iterator i = xacts.begin();
i != xacts.end();
i++)
remember_xact(**i);
}
void walk_component_xacts(item_handler<transaction_t>& handler) const {
for (transactions_list::const_iterator i = component_xacts->begin();
i != component_xacts->end();
i++)
handler(**i);
}
}; };
inline bool transaction_has_xdata(const transaction_t& xact) { inline bool transaction_has_xdata(const transaction_t& xact) {
@ -323,6 +358,22 @@ class collapse_transactions : public item_handler<transaction_t>
virtual void operator()(transaction_t& xact); virtual void operator()(transaction_t& xact);
}; };
class component_transactions : public item_handler<transaction_t>
{
item_predicate<transaction_t> pred;
public:
component_transactions(item_handler<transaction_t> * handler,
const value_expr_t * predicate)
: item_handler<transaction_t>(handler), pred(predicate) {}
component_transactions(item_handler<transaction_t> * handler,
const std::string& predicate)
: item_handler<transaction_t>(handler), pred(predicate) {}
virtual void operator()(transaction_t& xact);
};
class related_transactions : public item_handler<transaction_t> class related_transactions : public item_handler<transaction_t>
{ {
transactions_list transactions; transactions_list transactions;
@ -379,8 +430,10 @@ class changed_value_transactions : public item_handler<transaction_t>
class subtotal_transactions : public item_handler<transaction_t> class subtotal_transactions : public item_handler<transaction_t>
{ {
struct acct_value_t { struct acct_value_t {
account_t * account; account_t * account;
value_t value; value_t value;
transactions_list components;
acct_value_t(account_t * a) : account(a) {} acct_value_t(account_t * a) : account(a) {}
acct_value_t(account_t * a, value_t& v) : account(a), value(v) {} acct_value_t(account_t * a, value_t& v) : account(a), value(v) {}
@ -393,6 +446,7 @@ class subtotal_transactions : public item_handler<transaction_t>
protected: protected:
values_map values; values_map values;
bool remember_components;
std::list<entry_t> entry_temps; std::list<entry_t> entry_temps;
std::list<transaction_t> xact_temps; std::list<transaction_t> xact_temps;
@ -401,15 +455,16 @@ class subtotal_transactions : public item_handler<transaction_t>
std::time_t start; std::time_t start;
std::time_t finish; std::time_t finish;
subtotal_transactions(item_handler<transaction_t> * handler) subtotal_transactions(item_handler<transaction_t> * handler,
: item_handler<transaction_t>(handler), start(0), finish(0) {} bool _remember_components = false)
: item_handler<transaction_t>(handler),
remember_components(_remember_components), start(0), finish(0) {}
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
subtotal_transactions(const subtotal_transactions&) { subtotal_transactions(const subtotal_transactions&) {
assert(0); assert(0);
} }
#endif #endif
virtual ~subtotal_transactions() {
~subtotal_transactions() {
clear_entries_transactions(entry_temps); clear_entries_transactions(entry_temps);
} }
@ -442,9 +497,11 @@ class interval_transactions : public subtotal_transactions
public: public:
interval_transactions(item_handler<transaction_t> * _handler, interval_transactions(item_handler<transaction_t> * _handler,
const interval_t& _interval, const interval_t& _interval,
const value_expr_t * sort_order = NULL) const value_expr_t * sort_order = NULL,
: subtotal_transactions(_handler), interval(_interval), bool remember_components = false)
last_xact(NULL), started(false), sorter(NULL) { : subtotal_transactions(_handler, remember_components),
interval(_interval), last_xact(NULL), started(false),
sorter(NULL) {
if (sort_order) { if (sort_order) {
sorter = new sort_transactions(handler, sort_order); sorter = new sort_transactions(handler, sort_order);
handler = sorter; handler = sorter;
@ -452,15 +509,16 @@ class interval_transactions : public subtotal_transactions
} }
interval_transactions(item_handler<transaction_t> * _handler, interval_transactions(item_handler<transaction_t> * _handler,
const std::string& _interval, const std::string& _interval,
const std::string& sort_order = "") const std::string& sort_order = "",
: subtotal_transactions(_handler), interval(_interval), bool remember_components = false)
last_xact(NULL), started(false), sorter(NULL) { : subtotal_transactions(_handler, remember_components),
interval(_interval), last_xact(NULL), started(false),
sorter(NULL) {
if (! sort_order.empty()) { if (! sort_order.empty()) {
sorter = new sort_transactions(handler, sort_order); sorter = new sort_transactions(handler, sort_order);
handler = sorter; handler = sorter;
} }
} }
virtual ~interval_transactions() { virtual ~interval_transactions() {
if (sorter) if (sorter)
delete sorter; delete sorter;
@ -482,11 +540,13 @@ class by_payee_transactions : public item_handler<transaction_t>
typedef std::pair<std::string, subtotal_transactions *> payee_subtotals_pair; typedef std::pair<std::string, subtotal_transactions *> payee_subtotals_pair;
payee_subtotals_map payee_subtotals; payee_subtotals_map payee_subtotals;
bool remember_components;
public: public:
by_payee_transactions(item_handler<transaction_t> * handler) by_payee_transactions(item_handler<transaction_t> * handler,
: item_handler<transaction_t>(handler) {} bool _remember_components = false)
: item_handler<transaction_t>(handler),
remember_components(_remember_components) {}
virtual ~by_payee_transactions(); virtual ~by_payee_transactions();
virtual void flush(); virtual void flush();
@ -514,8 +574,9 @@ class dow_transactions : public subtotal_transactions
transactions_list days_of_the_week[7]; transactions_list days_of_the_week[7];
public: public:
dow_transactions(item_handler<transaction_t> * handler) dow_transactions(item_handler<transaction_t> * handler,
: subtotal_transactions(handler) {} bool remember_components = false)
: subtotal_transactions(handler, remember_components) {}
virtual void flush(); virtual void flush();
virtual void operator()(transaction_t& xact) { virtual void operator()(transaction_t& xact) {