Skip to content

Instantly share code, notes, and snippets.

@patefacio
Last active May 22, 2018 20:10
Show Gist options
  • Save patefacio/d861650e7297b4d27c6e0c591a43cb12 to your computer and use it in GitHub Desktop.
Save patefacio/d861650e7297b4d27c6e0c591a43cb12 to your computer and use it in GitHub Desktop.
Perf Issue HTML only
bash-3.2$ time curl -H "Accept: text/html" http://localhost:8002/forecast/214/1116543?use_item_assumptions=true > /tmp/goo.html
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 321k 100 321k 0 0 168k 0 0:00:01 0:00:01 --:--:-- 168k
real 0m1.921s
user 0m0.008s
sys 0m0.006s
bash-3.2$ time curl http://localhost:8002/forecast/214/1116543?use_item_assumptions=true > /tmp/goo.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 657k 100 657k 0 0 32.6M 0 --:--:-- --:--:-- --:--:-- 33.8M
real 0m0.032s
user 0m0.008s
sys 0m0.007s
bash-3.2$ time curl -H "Accept: text/html" -skw "\ntime_connect: %{time_connect}s\ntime_namelookup: %{time_namelookup}s\ntime_pretransfer: %{time_pretransfer}\ntime_starttransfer: %{time_starttransfer}s\ntime_redirect: %{time_redirect}s\ntime_total: %{time_total}s\n\n" -Lo /tmp/goo.html http://localhost:8002/forecast/214/1116543?use_item_assumptions=true
time_connect: 0.004878s
time_namelookup: 0.004677s
time_pretransfer: 0.004906
time_starttransfer: 2.004142s
time_redirect: 0.000000s
time_total: 2.004880s
real 0m2.017s
user 0m0.007s
sys 0m0.006s
bash-3.2$ time curl -skw "\ntime_connect: %{time_connect}s\ntime_namelookup: %{time_namelookup}s\ntime_pretransfer: %{time_pretransfer}\ntime_starttransfer: %{time_starttransfer}s\ntime_redirect: %{time_redirect}s\ntime_total: %{time_total}s\n\n" -Lo /tmp/goo.json http://localhost:8002/forecast/214/1116543?use_item_assumptions=true
time_connect: 0.004826s
time_namelookup: 0.004578s
time_pretransfer: 0.004871
time_starttransfer: 0.020730s
time_redirect: 0.000000s
time_total: 0.022098s
real 0m0.035s
user 0m0.008s
sys 0m0.007s
bash-3.2$
//! Collection of tera filters
// --- module imports ---
extern crate separator;
// --- module use statements ---
use rocket_contrib::tera::{self, to_value, Value};
use rocket_contrib::{Engines, Template};
use self::separator::Separatable;
use std::collections::HashMap;
// --- module function definitions ---
/// Register all custom tera filters
///
/// * `engines` - Engines, to add some tera filters
///
pub fn register_filters(engines: &mut Engines) -> () {
info!("Registering tera filters");
engines.tera.register_filter("as_number", as_number);
engines.tera.register_filter("as_money", as_money);
engines.tera.register_filter("as_percent", as_percent);
engines.tera.register_filter(
"sheltered_qualified_dividends",
sheltered_qualified_dividends,
);
}
/// Formats an integer with commas
///
/// * `value` - Value being transformed
/// * `args` - Arguments to filter
/// * _return_ - Transformed value
///
fn as_number(value: Value, _args: HashMap<String, Value>) -> tera::Result<Value> {
let result: Value;
// custom <fn as_number>
use serde_json::from_value;
let value: i64 = match from_value::<i64>(value.clone()) {
Ok(s) => s,
Err(_) => {
return Err(format!(
"Filter `{}` was called on an incorrect value: got `{}` but expected a {}",
"as_number",
value,
stringify!(i64)
).into());
}
};
result = to_value(if value > 0 {
format!("{}", value.separated_string())
} else if value < 0 {
format!("({})", (-value).separated_string())
} else {
"_".to_string()
}).unwrap();
// end <fn as_number>
Ok(result)
}
/// Formats floating point input as money with consisting rounding, commas, etc
///
/// * `value` - Value being transformed
/// * `args` - Arguments to filter
/// * _return_ - Transformed value
///
fn as_money(value: Value, _args: HashMap<String, Value>) -> tera::Result<Value> {
let result: Value;
// custom <fn as_money>
use serde_json::from_value;
let value: f64 = match from_value::<f64>(value.clone()) {
Ok(s) => s,
Err(_) => {
return Err(format!(
"Filter `{}` was called on an incorrect value: got `{}` but expected a {}",
"as_money",
value,
stringify!(f64)
).into());
}
};
result = to_value(if value > 0.0 {
format!("${}", value.separated_string())
} else if value < 0.0 {
format!("$({})", (-value).separated_string())
} else {
"_".to_string()
}).unwrap();
// end <fn as_money>
Ok(result)
}
/// Formats floating point input as a percentage
///
/// * `value` - Value being transformed
/// * `args` - Arguments to filter
/// * _return_ - Transformed value
///
fn as_percent(value: Value, _args: HashMap<String, Value>) -> tera::Result<Value> {
let result: Value;
// custom <fn as_percent>
use serde_json::from_value;
let value: f64 = match from_value::<f64>(value.clone()) {
Ok(s) => s,
Err(_) => {
return Err(format!(
"Filter `{}` was called on an incorrect value: got `{}` but expected a {}",
"as_money",
value,
stringify!(f64)
).into());
}
};
result = to_value(format!("{:.2}%", value*100.0)).unwrap();
// end <fn as_percent>
Ok(result)
}
/// Returns list of sheltered qualified dividends from `Worksheet` if any, or empty list if none
///
/// * `value` - Value being transformed
/// * `args` - Arguments to filter
/// * _return_ - Transformed value
///
fn sheltered_qualified_dividends(
value: Value, _args: HashMap<String, Value>
) -> tera::Result<Value> {
let result: Value;
// custom <fn sheltered_qualified_dividends>
result = to_value("TODO!!").unwrap();
// end <fn sheltered_qualified_dividends>
Ok(result)
}
{% import "worksheets" as worksheets %}
{% import "balance_sheet_proof" as bsp %}
{% import "forecast_config" as forecast_config %}
{% import "core" as core %}
{% import "end_balance_streams" as end_balance_streams %}
{% import "assumption_details" as assumption_details %}
{% set start_year = year_range.start %}
{% set end_year = year_range.end %}
{% set num_years = end_year - start_year %}
{% set forecast_config = forecast_details.forecast_config %}
{% set bsps = forecast_details.balance_sheet_proofs %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>"{{ forecast_details.dossier_name }}" Forecast</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
</head>
<body style="background-color: rgb(255,255,204,0.2);">
<div class="card-body" style="width: 60rem;">
<h5 class="card-title">
{% if forecast_id == 0 %}
<i>Geometric Mean Forecast</i>
{% else %}
<i>Random Forecast (id={{forecast_id | as_number}})</i>
{% endif %}
</h5>
<div class="container">
<div class="row">
<div class="col-sm">
<div class="container">
<div class="card">
<div class="card-body">
<h4 class="card-title">Forecast Details</h4>
<table class="user-table table-sm table-striped">
{% if forecast_details %}
<tr>
<td nowrap><strong>Dossier<strong></td>
<td nowrap><em>{{ forecast_details.dossier_name }} ({{dossier_id | as_number}})</em></td>
</tr>
<tr>
<td><strong>Duration:</strong></td>
<td>{{ forecast_details.forecast_millis | as_number }} ms</td>
</tr>
{% endif %}
{{ forecast_config::configured_range(forecast_config=forecast_config) }}
{{ forecast_config::year_range(range=year_range, label="Evaluated Range") }}
<td nowrap>Forecast Tax Override</td>
<td nowrap>{{ forecast_config.forecast_tax_treatment }}</td>
</tr>
<tr>
<td nowrap>Primary User</td>
<td nowrap>{{ forecast_config.primary_user }}</td>
</tr>
<tr>
<td nowrap>Growth Outlook</td>
<td nowrap>{{ forecast_config.growth_outlook }}</td>
</tr>
<tr>
<td nowrap>Inflation</td>
<td>{{ core::rate_curve_pct(rc=forecast_config.inflation) }}</td>
</tr>
<tr>
<td nowrap>Cost of Capital</td>
<td>{{ core::rate_curve_pct(rc=forecast_config.cost_of_capital) }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
{% if forecast_details %}
<div class="col-sm">
<div class="container">
{% set user_markers = forecast_details.year_markers.user_markers %}
{% if user_markers %}
<div class="card">
<div class="card-body">
<h4 class="card-title">Person Events</h4>
<table class="user-year-markers-table table-sm table-striped">
<tr>
<th>Person</th>
<th nowrap>Event</th>
<th nowrap>Age/Year</th>
</tr>
{% for uym in user_markers %}
<tr>
<th nowrap>{{uym.user}}</th>
<th nowrap>
{{ uym.user_year_marker_type }}
</th>
<th nowrap>{{uym.year - uym.birth_year}}/{{uym.year}}</th>
</tr>
{% endfor %}
</table>
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
{% set end_balance_streams = forecast_details.end_balance_streams %}
{% set worksheets = forecast_details.worksheets %}
{% set inflation = forecast_details.inflation * 100.0 %}
<table class="end-balance-table table-sm table-striped">
<tr><td class="table-success" colspan="5" style="text-align:center; border-bottom:2px solid blue; border-top: 2px solid blue;"><h4>Balances Sheet Items</h4></td></tr>
<theader>
<tr>
<th>Item</th>
<th nowrap>Growth Item</th>
<th >Assumption Source</th>
<th >Growth Assumption</th>
<th style="text-align:right">Realized Log-Ret</th>
<th >PV Final Year @ {{inflation | round(precision=2)}}%</th>
<th style="text-align:right">Initial</th>
{{ worksheets::year_headers(worksheets=worksheets)}}
</tr>
</theader>
<tbody>
{% if forecast_details.item_identifiers.worths %}
<tr><td class="table-warning" colspan="5" style="text-align:center; border-bottom:2px solid blue; border-top: 2px solid blue;"><em>Non Financial Assets</em></td></tr>
{% endif %}
{% for ebs in end_balance_streams %}
{% if forecast_details.item_identifiers.worths | length == loop.index - 1 %}
<tr><td class="table-warning" colspan="5" style="text-align:center; border-bottom:2px solid blue; border-top: 2px solid blue;"><em>Financial Assets</em></td></tr>
{% endif %}
{% set ad = ebs.assumption_details %}
<tr>
<td nowrap>{{ad.id}}</td>
{{ assumption_details::assumption_details(assumption_details=ad) }}
{% if ad.growth_assumption_details %}
{{ assumption_details::resolved_growth_source(growth_assumption_details=ad.growth_assumption_details) }}
{{ assumption_details::normal(normal_spec=ad.growth_assumption_details.growth.growth_assumption.normal_spec) }}
{% endif %}
{% set log_ret = ebs.log_returns * 100.0 / num_years %}
<td nowrap style="text-align:right">{{ log_ret | round(precision=3) }} %</td>
<td style="text-align:right; border-right: 2px solid blue;">{{ebs.pv_end_balance | round(precision=0) | as_money }}</td>
<td style="text-align:right"> {{ ebs.initial_balance | round(precision=0) | as_money }} </td>
{{ end_balance_streams::end_balances(end_balance_streams=ebs) }}
</tr>
{% endfor %}
<tr class="table-info" ><td colspan="5" style="border-bottom: 2px solid green;"></td></tr>
{{ worksheets::label_row(label="Basic Allocation") }}
{{ worksheets::end_balance_detail_inset_row(label="Stock", table_row=worksheets::stock_allocation(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Bond", table_row=worksheets::bond_allocation(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Cash", table_row=worksheets::cash_allocation(worksheets=worksheets)) }}
{# {{ worksheets::end_balance_detail_inset_row(label="Other", table_row=worksheets::other_allocation(worksheets=worksheets)) }} #}
{{ worksheets::net_worth_row(pv_end_value=forecast_details.pv_end_net_value, worksheets=worksheets) }}
<tr><td class="table-success" colspan="5" style="text-align:center; border-bottom:2px solid blue; border-top: 2px solid blue;"><h4>Annual Details</h4></td></tr>
{% if forecast_details.total_sheltered_distributions != 0.0 %}
{{ worksheets::label_row(label="Sheltered Distributions") }}
{{ worksheets::end_balance_detail_inset_row(label="Sheltered Qualified Divs", table_row=worksheets::sheltered_qualified_dividends(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Sheltered Unqualified Divs", table_row=worksheets::sheltered_unqualified_dividends(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Sheltered Cap Gain Dist", table_row=worksheets::sheltered_capital_gain_distributions(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Sheltered Interest", table_row=worksheets::sheltered_interest(worksheets=worksheets)) }}
{% endif %}
{% if forecast_details.total_taxable_distributions != 0.0 %}
{{ worksheets::label_row(label="Non-Sheltered Distributions") }}
{{ worksheets::end_balance_detail_inset_row(label="Qualified Divs", table_row=worksheets::qualified_dividends(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Unqualified Divs", table_row=worksheets::unqualified_dividends(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Cap Gain Dist", table_row=worksheets::capital_gain_distributions(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Interest", table_row=worksheets::interest(worksheets=worksheets)) }}
{% endif %}
{{ worksheets::label_row(label="Taxable Distributions By Treatment") }}
{{ worksheets::end_balance_detail_inset_row(label="Reinvested", table_row=worksheets::reinvested(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Disbursed", table_row=worksheets::disbursed(worksheets=worksheets)) }}
{{ worksheets::label_row(label="Capital Gains/Losses") }}
{{ worksheets::end_balance_detail_row(label="Long Term Cap Gains/(Loss)", table_row=worksheets::long_term_capital_gains(worksheets=worksheets)) }}
{% if forecast_details.total_penalties != 0.0 %}
{{ worksheets::label_row(label="Loss Carry Forward Details") }}
{{ worksheets::end_balance_detail_row(label="Prior Losses Available", table_row=worksheets::prior_losses_available(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_row(label="Losses Offsetting Gains", table_row=worksheets::losses_offsetting_gains(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_row(label="Losses Offsetting Ord Inc", table_row=worksheets::losses_offsetting_ordinary_income(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_row(label="Remaining Losses", table_row=worksheets::remaining_losses(worksheets=worksheets)) }}
{% endif %}
{{ worksheets::end_balance_summary_row(label="Total Income", table_row=worksheets::total_income(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Earned Income", table_row=worksheets::earned_income(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Passive Income", table_row=worksheets::passive_income(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Portfolio Income", table_row=worksheets::portfolio_income(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Ordinary Income", table_row=worksheets::ordinary_income(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Required Min Dist", table_row=worksheets::rmd(worksheets=worksheets)) }}
{% if forecast_details.total_retirement_investments != 0.0 %}
{{ worksheets::end_balance_detail_inset_row(label="Retirement Deductions", table_row=worksheets::retirement_deductions(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Retirement Investments", table_row=worksheets::retirement_contributions(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Employer Match", table_row=worksheets::employer_match(worksheets=worksheets)) }}
{% endif %}
{% if forecast_details.total_penalties != 0.0 %}
{{ worksheets::label_row(label="Penalties") }}
{{ worksheets::end_balance_detail_inset_row(label="College 529 Penalties", table_row=worksheets::college_penalties(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="HSA Penalties", table_row=worksheets::health_care_penalty(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Early Withdrawal Penalties", table_row=worksheets::early_withdrawal_penalty(worksheets=worksheets)) }}
{% endif %}
{{ worksheets::end_balance_summary_row(label="Adjusted Gross Income", table_row=worksheets::agi(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_row(label="Tax Basis", table_row=worksheets::tax_basis(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Standard Deduction", table_row=worksheets::standard_deduction(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_row(label="Marginal Rate", table_row=worksheets::marginal_tax_rates(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_row(label="Effective Rate", table_row=worksheets::effective_tax_rates(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_row(label="Total Tax Bill", table_row=worksheets::total_tax_bill(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Ordinary Tax Bill", table_row=worksheets::tax_bill(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Soc. Sec. Tax", table_row=worksheets::social_security_tax(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Medicare Tax", table_row=worksheets::medicare_tax(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Long Term Cap Gain Tax", table_row=worksheets::long_term_capital_gains_tax(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="On LTCG", table_row=worksheets::long_term_capital_gains(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="With LTCG Hurdle", table_row=worksheets::long_term_capital_gains_hurdle(worksheets=worksheets)) }}
{#
<tr><td class="table-primary" colspan="5" style="text-align:center; border-bottom:2px solid blue; border-top: 2px solid blue;"><h5>Flow Summary</h5></td></tr>
{{ worksheets::end_balance_summary_row(label="Net Flows", table_row=worksheets::net_flows(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Total Inflows", table_row=worksheets::inflows(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Total Outflows", table_row=worksheets::outflows(worksheets=worksheets)) }}
#}
{{ worksheets::end_balance_summary_row(label="Remaining Shortfall", table_row=worksheets::remaining_shortfalls(worksheets=worksheets)) }}
{{ worksheets::label_row(label="Balance Breakdown") }}
{{ worksheets::end_balance_detail_row(label="Start Balance", table_row=worksheets::summary_start_balances(worksheets=worksheets)) }}
{{ worksheets::end_balance_detail_inset_row(label="Total Increases", table_row=bsp::total_up(bsps=bsps)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Worth Growth", table_row=bsp::worth_growth(bsps=bsps)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Holding Growth", table_row=bsp::holding_growth(bsps=bsps)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Modeled Inflows", table_row=bsp::modeled_inflows(bsps=bsps)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Employer Match", table_row=bsp::employer_match(bsps=bsps)) }}
{{ worksheets::end_balance_detail_inset_row(label="Total Decreases", table_row=bsp::total_down(bsps=bsps)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Modeled Outflows", table_row=bsp::modeled_outflows(bsps=bsps)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Tax Bill", table_row=bsp::tax_bill(bsps=bsps)) }}
{{ worksheets::end_balance_detail_double_inset_row(label="Penalties", table_row=bsp::penalties(bsps=bsps)) }}
{{ worksheets::end_balance_detail_row(label="Proof End Balance", table_row=bsp::proof_end_balance(bsps=bsps)) }}
{% set cash_flow_streams = forecast_details.cash_flow_streams %}
{% if cash_flow_streams %}
<tr><td class="table-success" colspan="5" style="text-align:center; border-bottom:2px solid blue; border-top: 2px solid blue;"><h4>Modeled Cash Flows</h4></td></tr>
<tr>
<th>Flow</th>
<th nowrap>Growth Item</th>
<th >Assumption Source</th>
<th >Growth Assumption</th>
<th style="text-align:right">Realized Log-Ret</th>
<th >PV Sum @ {{inflation | round(precision=2)}}%</th>
<th style="text-align:right">Initial</th>
{{ worksheets::year_headers(worksheets=worksheets)}}
</tr>
{% for cfs in cash_flow_streams %}
{% set ad = cfs.assumption_details %}
<tr>
<td nowrap>{{ad.id}}</td>
{{ assumption_details::assumption_details(assumption_details=ad)}}
{% if ad.growth_assumption_details %}
{% if ad.growth_assumption_details.resolved_source == "DossierPinned" %}
<td nowrap>📌</td>
{% elif ad.growth_assumption_details.resolved_source == "DossierNormalSpec" %}
<td nowrap>📁</td>
{% else %}
<td nowrap>{{ad.growth_assumption_details.growth_source}}</td>
{% endif %}
{{ assumption_details::normal(normal_spec=ad.growth_assumption_details.growth.growth_assumption.normal_spec)}}
{% else %}
<td style="text-align:center">Fixed</td>
<td style="text-align:center">__</td>
<td style="text-align:center">__</td>
{% endif %}
{% if cfs.log_returns %}
<td nowrap style="text-align:right">{{ cfs.log_returns * 100.0 | round(precision=3)}} %</td>
{% else %}
<td nowrap style="text-align:right">_</td>
{% endif %}
<td nowrap style="text-align:right; border-right: 2px solid blue;">{{cfs.cumulative_pv | round(precision=0) | as_money }}</td>
<td nowrap style="text-align:right;">
{% if cfs.initial_value %}
{% if cfs.flow_direction == "InFlow" %}
{% set initial_value = cfs.initial_value.value %}
{% set arrow = "↑" %}
{% else %}
{% set initial_value = 0.0 - cfs.initial_value.value %}
{% set arrow = "↓" %}
{% endif %}
{{ initial_value | round(precision=0) | as_money }}:{{arrow}}
{% else %}
_
{% endif %}
</td>
{% for dv in cfs.values %}
<td style="text-align:right">{{dv | round(precision=0) | as_money}}</td>
{% endfor %}
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</body>
</html>
/// Get a forecast for a dossier using the standard `include_forecast_details` option.
///
/// This is a simple get version, without configuration options, of the `post_forecast` route.
///
/// * `dossier_id` - Id of `Dossier` to forecast
/// * `forecast_id` - Id of `Forecast` - 0 indicating _Geometric Mean Forecast_, otherwise a _Random Forecast_
/// * `get_forecast_query_parms` - Additional query parameters
/// * `data_layer` - Access to the `DataLayer` provided by `plus_persist`
/// * _return_ - The http response to request as html `Option<Template>`
///
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
#[get("/forecast/<dossier_id>/<forecast_id>?<get_forecast_query_parms>", rank = 1)]
fn get_forecast_html(
dossier_id: i32, forecast_id: u32, get_forecast_query_parms: GetForecastQueryParms,
data_layer: DataLayer,
) -> Option<Template> {
use stopwatch::Stopwatch;
let sw = Stopwatch::start_new();
let result = match get_forecast(
dossier_id,
forecast_id,
get_forecast_query_parms,
data_layer,
) {
Ok(forecast) => {
Some(Template::render("forecast", &forecast))
}
Err(err) => {
warn!("Unable to handle html request {}", err);
None
}
};
println!("Tera Timings {} ms", sw.elapsed_ms());
result
}
/// Get a forecast for a dossier using the standard `include_forecast_details` option.
///
/// This is a simple get version, without configuration options, of the `post_forecast` route.
///
/// * `dossier_id` - Id of `Dossier` to forecast
/// * `forecast_id` - Id of `Forecast` - 0 indicating _Geometric Mean Forecast_, otherwise a _Random Forecast_
/// * `get_forecast_query_parms` - Additional query parameters
/// * `data_layer` - Access to the `DataLayer` provided by `plus_persist`
/// * _return_ - The http response to request as json `Json<GetForecastReply>`
///
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
#[get("/forecast/<dossier_id>/<forecast_id>?<get_forecast_query_parms>",
format = "application/json", rank = 0)]
fn get_forecast_json(
dossier_id: i32, forecast_id: u32, get_forecast_query_parms: GetForecastQueryParms,
data_layer: DataLayer,
) -> Json<GetForecastReply> {
let mut _rest_timing_guard = ForecastTimingGuard::new(
"TODO".into(),
RestTimingType::GetTiming,
RestObjectType::Forecast,
);
match get_forecast(
dossier_id,
forecast_id,
get_forecast_query_parms,
data_layer,
) {
Ok(forecast) => {
// custom <get_forecast_json_timing>
if let Some(ref forecast_details) = forecast.forecast_details {
_rest_timing_guard
.forecast_timing_entry
.forecast_scale_details =
forecast_details.get_forecast_scale_details();
}
// end <get_forecast_json_timing>
Json(GetForecastReply {
forecast: Some(forecast),
error_details: None,
})
}
Err(err) => Json(GetForecastReply {
forecast: None,
error_details: Some(ErrorDetails {
error_message: format!("{}", err),
}),
}),
}
}
/// A forecast of a dossier
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct Forecast {
/// Database id of the dossier
#[serde(skip_serializing_if = "is_default")]
pub dossier_id: Option<i32>,
/// Identifier for this forecast.
///
/// The `forecast_id` is used to seed the random generator, to provide
/// reproducible forecasts.
///
pub forecast_id: u32,
/// Range for the forecast - may differ from input config due to bounds check
pub year_range: YearRange,
/// Net worth at end of forecast
pub end_net_worth: f64,
/// Present value of net worth at end of forecast
pub pv_end_net_worth: f64,
/// Mean of _annual_ difference between all _incomes_ and _expenses_
pub mean_cash_flow_delta: f64,
/// Details of the forecast
#[serde(skip_serializing_if = "is_default")]
pub forecast_details: Option<Box<ForecastDetails>>,
}
/// Additional information provided when detailed results from forecast desired
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct ForecastDetails {
/// Name of the `Dossier`
pub dossier_name: String,
/// Config used to generate forecast - only set if `include_forecast_details` is true
pub forecast_config: ForecastConfig,
/// Identifiers for items in `Dossier`
pub item_identifiers: ItemIdentifiers,
/// User specified and inferred funding links
pub funding_links: MapOfStrToArrayOfStr,
/// Number of years in forecast
pub num_years: u32,
/// What was used to calculate taxes
pub forecast_tax_determinants: ForecastTaxDeterminants,
/// List of important years of the forecast
pub year_markers: YearMarkers,
/// List of worksheets
pub worksheets: Vec<Worksheet>,
/// Cash flow streams, returned if `include_cash_streams` requested
pub cash_flow_streams: Vec<CashFlowStream>,
/// *End Balances* of `Worth` and `Holdings`, returned if `include_balance_streams` requested
pub end_balance_streams: Vec<EndBalanceStream>,
/// User specified number for inflation rate to discount future values
pub inflation: f64,
/// Present value (discounted using `ForecastConfig::inflation`) of final net worth - for perspective
pub pv_end_net_value: f64,
/// Number of millis required to perform forecast
pub forecast_millis: i64,
/// Second accounting of end balance
pub balance_sheet_proofs: Vec<BalanceSheetProof>,
/// Sum of all penalties across all years in current dollars
pub total_penalties: f64,
/// Sum of all sheltered distributions across all years in current dollars
pub total_sheltered_distributions: f64,
/// Sum of all taxable distributions across all years in current dollars
pub total_taxable_distributions: f64,
/// Sum of all losses across all years in current dollars
pub total_losses: f64,
/// Sum of all retirement investments across all years in current dollars
pub total_retirement_investments: f64,
}
/// Object containing details of _EOY_ processing
#[derive(Clone, Default, Deserialize, PartialEq, Serialize)]
pub struct Worksheet {
/// Year for calculations
pub year: Year,
/// Entry for the inflation curve
pub inflation_rate_entry: CurveRateEntry,
/// Entry for the cost of capital curve
pub cost_of_capital_rate_entry: CurveRateEntry,
/// Age of primary owner
#[serde(skip_serializing_if = "is_default")]
pub primary_owner_age: Option<Year>,
/// Netting in/out flows.
pub netting: Netting,
/// Tax statement for the year
pub fed_tax_statement: FedTaxStatement,
/// Tax bill for prior year
pub tax_bill: f64,
/// Annual _net flows_, _obligations_ if cash flows are negative, _investment_ if positive.
///
/// This is the sum of `inflows` and `outflows` plus `disbursed` less the `tax_bill`.
///
pub net_flows: f64,
/// Any remaining shortfall after what can be sold has been sold
pub remaining_shortfall: f64,
/// Summary of draw-down, investment over the period
pub liquidation_summary: LiquidationSummary,
/// The taxable positions for each holding
pub taxable_positions: Vec<TaxablePosition>,
/// Penalties occurring in the year
pub penalties: Penalties,
/// Current allocation across types
pub basic_allocation: BasicAllocation,
/// Target allocation
#[serde(skip_serializing_if = "is_default")]
pub target_allocation: Option<BasicAllocation>,
}
/// A single entry in a position, representing a purchase at specific price
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct TaxLot {
/// Amount purchased/sold in lot
quantity: f64,
/// Price of one unit for lot
price: f64,
/// When lot was created (year of purchase/sale)
year: Year,
}
/// Tracks lotting associated with a `Holding` position
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct TaxablePosition {
/// Position sum of all lots
total_position: f64,
/// Total cost for all lots in position
total_cost_basis: f64,
/// List of tax lots.
///
/// This is the history of the position and will be tracked iff
/// `TaxablePosition` is created with `from_first_lot_with_history`
///
#[serde(skip_serializing_if = "is_default")]
tax_lots: Option<Vec<TaxLot>>,
/// Record of most recent mark price
mark_price: f64,
}
/// Basic allocation among three categories (stock, bond, cash)
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct BasicAllocation {
/// Allocation to stocks
stock: f64,
/// Allocation to bonds
bond: f64,
/// Allocation to cash
cash: f64,
/// Allocation to other
other: f64,
}
/// Defines set of target allocations
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct GlidePath {
/// List of target allocations
pub target_allocations: Vec<TargetAllocation>,
}
/// Specifies a desired allocation for a specific year
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct TargetAllocation {
/// Year for an allocation
pub year: Year,
/// A basic target allocation for a specific year
pub basic_allocation: BasicAllocation,
}
{% macro money_row(row, show_empty) %}
{% for cell in row %}
<td style="text-align:right">{{ cell | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro money_row %}
{% macro qualified_dividends(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.taxable_distributions.qualified | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro qualified_dividends %}
{% macro unqualified_dividends(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.taxable_distributions.unqualified | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro unqualified_dividends %}
{% macro capital_gain_distributions(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.taxable_distributions.capital_gain_distribution | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro capital_gain_distributions %}
{% macro interest(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.taxable_distributions.interest | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro interest %}
{% macro reinvested(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.taxable_distributions.reinvested | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro reinvested %}
{% macro disbursed(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.taxable_distributions.disbursed | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro disbursed %}
{# sheltered versions #}
{% macro sheltered_qualified_dividends(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" class="table-info">{{worksheet.netting.sheltered_distributions.qualified | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro sheltered_qualified_dividends %}
{% macro sheltered_unqualified_dividends(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" class="table-info">{{worksheet.netting.sheltered_distributions.unqualified | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro sheltered_unqualified_dividends %}
{% macro sheltered_capital_gain_distributions(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" class="table-info">{{worksheet.netting.sheltered_distributions.capital_gain_distribution | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro sheltered_capital_gain_distributions %}
{% macro sheltered_interest(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" class="table-info">{{worksheet.netting.sheltered_distributions.interest | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro sheltered_interest %}
{% macro sheltered_reinvested(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" class="table-info">{{worksheet.netting.sheltered_distributions.reinvested | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro sheltered_reinvested %}
{% macro sheltered_disbursed(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" class="table-info">{{worksheet.netting.sheltered_distributions.disbursed | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro sheltered_disbursed %}
{# end sheltered versions #}
{% macro long_term_capital_gains_tax(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.fed_tax_statement.long_term_capital_gains_tax | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro long_term_capital_gains_tax %}
{% macro long_term_capital_gains(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.tax_inputs.long_term_capital_gains | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro long_term_capital_gains %}
{% macro long_term_capital_gains_hurdle(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.long_term_capital_gains_hurdle | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro long_term_capital_gains_hurdle %}
{% macro passive_income(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.tax_inputs.passive_income | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro passive_income %}
{% macro portfolio_income(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.tax_inputs.portfolio_income | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro portfolio_income %}
{% macro total_income(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.total_income | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro total_income %}
{% macro ordinary_income(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.tax_inputs.ordinary_income | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro ordinary_income %}
{% macro earned_income(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.tax_inputs.earned_income | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro earned_income %}
{% macro agi(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.adjusted_gross_income | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro agi %}
{% macro retirement_deductions(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.deductible_retirement_investment | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro retirement_deductions %}
{% macro sheltered_long_term_capital_gains(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" class="table-info">{{worksheet.netting.sheltered_long_term_capital_gain | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro long_term_capital_gains %}
{% macro prior_losses_available(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.prior_losses_available | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro prior_losses_available %}
{% macro losses_offsetting_ordinary_income(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.losses_offsetting_ordinary_income | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro losses_offsetting_ordinary_income %}
{% macro losses_offsetting_gains(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.losses_offsetting_gains | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro losses_offsetting_gains %}
{% macro remaining_losses(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.fed_tax_statement.remaining_losses_available | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro remaining_losses %}
{% macro end_balances(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.period_balance.end_balance | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro end_balances %}
{% macro tax_basis(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ worksheet.fed_tax_statement.tax_basis | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro tax_basis %}
{% macro tax_bill(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.fed_tax_statement.tax_bill | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro tax_bill %}
{% macro total_tax_bill(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right; border-bottom-style: solid;">{{ -1.0 * worksheet.fed_tax_statement.total_tax_bill | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro total_tax_bill %}
{% macro social_security_tax(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.fed_tax_statement.social_security_tax | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro social_security_tax %}
{% macro medicare_tax(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.fed_tax_statement.medicare_tax | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro medicare_tax %}
{% macro standard_deduction(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.fed_tax_statement.standard_deduction | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro standard_deduction %}
{% macro inflows(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.inflows | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro inflows %}
{% macro outflows(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ 0.0 - worksheet.netting.outflows | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro outflows %}
{% macro rmd(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.required_minimum_distribution | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro rmd %}
{% macro retirement_contributions(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.retirement_investment.employee_contribution | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro retirement_contributions %}
{% macro employer_match(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.retirement_investment.employer_match | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro employer_match %}
{% macro college_penalties(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.penalties.college_irs529_penalty | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro college_penalties %}
{% macro early_withdrawal_penalty(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.penalties.early_withdrawal_penalty | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro early_withdrawal_penalty %}
{% macro health_care_penalty(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ -1.0 * worksheet.penalties.health_care_penalty | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro health_care_penalty %}
{% macro net_flows(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.net_flows | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro outflows %}
{% macro summary_start_balances(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.period_balance.start_balance | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro outflows %}
{% macro summary_end_balances(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.period_balance.end_balance | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro outflows %}
{% macro summary_balance_with_growth(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{worksheet.netting.balance_with_growth | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro outflows %}
{% macro remaining_shortfalls(worksheets) %}
{% for worksheet in worksheets %}
{% if worksheet.remaining_shortfall > 0.0 %}
<td style="text-align:right" class="table-danger">
{% else %}
<td style="text-align:right">
{% endif %}
{{worksheet.remaining_shortfall | round(precision=0) | as_money }}</td>
{% endfor %}
{% endmacro outflows %}
{% macro year_headers(worksheets) %}
{% for worksheet in worksheets %}
<th style="text-align:right">
{{ worksheet.year }}
{% if worksheet.primary_owner_age %}
<div style="whitespace: nowrap">(Age:{{ worksheet.primary_owner_age }})</div>
{% endif %}
</th>
{% endfor %}
{% endmacro outflows %}
{% macro marginal_tax_rates(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ worksheet.fed_tax_statement.marginal_tax_rate | round(precision=2) | as_percent }}</td>
{% endfor %}
{% endmacro marginal_tax_rates %}
{% macro as_pct_or_null(v) %}
{% if v == 0 %}
_
{% else %}
{{ v | as_percent }}
{% endif %}
{% endmacro as_pct_or_null %}
{% macro stock_allocation(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ self::as_pct_or_null(v=worksheet.basic_allocation.stock) }}</td>
{% endfor %}
{% endmacro stock_allocation %}
{% macro bond_allocation(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ self::as_pct_or_null(v=worksheet.basic_allocation.bond) }}</td>
{% endfor %}
{% endmacro bond_allocation %}
{% macro cash_allocation(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ self::as_pct_or_null(v=worksheet.basic_allocation.cash) }}</td>
{% endfor %}
{% endmacro cash_allocation %}
{% macro other_allocation(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right">{{ self::as_pct_or_null(v=worksheet.basic_allocation.other) }}</td>
{% endfor %}
{% endmacro other_allocation %}
{% macro effective_tax_rates(worksheets) %}
{% for worksheet in worksheets %}
<td style="text-align:right" nowrap>
{% set total_income = worksheet.fed_tax_statement.total_income +
worksheet.fed_tax_statement.tax_inputs.qualified_dividends +
worksheet.fed_tax_statement.tax_inputs.long_term_capital_gains
%}
{% if worksheet.fed_tax_statement.total_tax_bill > 0.0 and total_income > 0.0 %}
{% set effective_rate = worksheet.fed_tax_statement.total_tax_bill / total_income %}
{{ effective_rate | round(precision=6) | as_percent }}
{% else %}
__
{% endif %}
</td>
{% endfor %}
{% endmacro outflows %}
{% macro label_row(label) %}
<tr>
<td>_</td>
<td>_</td>
<td>_</td>
<td>_</td>
<td nowrap><h5>{{ label }}</h5></td>
<td style="border-right: 2px solid blue;"></td>
</tr>
{% endmacro end_balance_detail_row %}
{% macro end_balance_detail_row(label, table_row) %}
<tr>
<td>_</td>
<td>_</td>
<td>_</td>
<td>_</td>
<td style="text-indent: 20px" nowrap><em>{{ label }}</em></td>
<td style="border-right: 2px solid blue;"></td>
<td nowrap></td>
{{ table_row | safe }}
</tr>
{% endmacro end_balance_detail_row %}
{% macro end_balance_detail_inset_row(label, table_row) %}
<tr>
<td>_</td>
<td>_</td>
<td>_</td>
<td>_</td>
<td style="text-indent: 30px" nowrap><em>{{ label }}</em></td>
<td style="border-right: 2px solid blue;"></td>
<td nowrap></td>
{{ table_row | safe }}
</tr>
{% endmacro end_balance_detail_inset_row %}
{% macro end_balance_detail_double_inset_row(label, table_row) %}
<tr>
<td>_</td>
<td>_</td>
<td>_</td>
<td>_</td>
<td style="text-indent: 60px" nowrap><em>{{ label }}</em></td>
<td style="border-right: 2px solid blue;"></td>
<td nowrap></td>
{{ table_row | safe }}
</tr>
{% endmacro end_balance_detail_double_inset_row %}
{% macro end_balance_summary_row(label, table_row) %}
<tr>
<td>_</td>
<td>_</td>
<td>_</td>
<td>_</td>
<td nowrap><h5>{{ label }}</h5></td>
<td style="border-right: 2px solid blue";></td>
<td nowrap></td>
{{ table_row | safe }}
</tr>
{% endmacro end_balance_summary_row %}
{% macro net_worth_row(pv_end_value, worksheets) %}
<tr class="table-warning">
<td>_</td>
<td>_</td>
<td>_</td>
<td>_</td>
<td><h5>Net Worth</h5></td>
<td style="text-align:right; border-right: 2px solid blue;">{{ pv_end_value | round(precision=0) | as_money}}</td>
{% for worksheet in worksheets | slice(start=0, end=1) %}
<td style="text-align:right;">{{ worksheet.netting.period_balance.start_balance | round(precision=0) | as_money }}</td>
{% endfor %}
{{ self::end_balances(worksheets=worksheets) | safe }}
</tr>
{% endmacro net_worth_row %}
@patefacio
Copy link
Author

screen shot 2018-05-22 at 2 24 54 pm

Slowdown does seem to be in tera processing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment