Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/akevalion/life_cost/llms.txt

Use this file to discover all available pages before exploring further.

Life Cost includes two D3-powered charts that turn your transaction history into visual summaries. The line chart plots a cumulative running total day by day over the full lifetime of the active wallet (using subtraction, so income days push the line down and expense days push it up), and the pie/donut chart breaks down the overall split between credits and debits. Both charts are scoped to current_user.last_visited_wallet_id and adjust for the client’s local timezone so day boundaries match what you see in the calendar.

Opening the Chart Modal

A Mostrar Gráfico button in the page header opens the #chartModal overlay. Clicking the overlay background or the × button closes it and returns to the main page. When the modal opens, showChart() in chart.js fires immediately:
function showChart() {
    $('#chartModal').show();
    $('#overlay').show();
    $('#chart').empty();

    const data = { 'timeZone': Intl.DateTimeFormat().resolvedOptions().timeZone };
    $.ajax({
        url: '/chart_data',
        method: 'POST',
        data: JSON.stringify(data),
        contentType: 'application/json',
        success: function (chartData) {
            // accumulate running totals, then build the SVG line chart
            buildChart(chartData);
        }
    });
}

Line Chart — Daily Balance Over Time

Endpoint: POST /chart_data
Request body: { "timeZone": "<IANA timezone string>" }
The server fetches every MoneyTransfer for the active wallet, groups them by local calendar date, and returns an array of { Date, Close } objects sorted chronologically:
@app.route("/chart_data", methods=['POST'])
@login_required
def chart_data():
    data = request.get_json()
    client_tz = pytz.timezone(data.get('timeZone', 'SYSTEM'))

    transfers = MoneyTransfer.query.filter(
        MoneyTransfer.wallet_id == current_user.last_visited_wallet_id
    ).all()

    daily_totals = {}
    for transfer in transfers:
        local_date = transfer.created_at.replace(tzinfo=pytz.utc).astimezone(client_tz).date()
        daily_totals[local_date] = daily_totals.get(local_date, 0) + transfer.amount

    days = [{'Date': date.isoformat(), 'Close': total}
            for date, total in sorted(daily_totals.items())]
    return jsonify(days)
The client then accumulates a running total before rendering — each day’s Close value is computed by subtracting the current day’s net amount from the previous running value:
let lastValue = 0;
chartData.forEach(item => {
    item.Date = new Date(item.Date);
    lastValue = item.Close = lastValue - item.Close;
});
buildChart(chartData);
The resulting SVG uses a d3.scaleUtc x-axis for dates and a d3.scaleLinear y-axis for the monetary value. Hovering the chart shows a tooltip with the exact date and balance value; clicking a data point closes the modal and loads that day’s transactions into the main table.
The line chart shows a cumulative running total computed by subtracting each day’s net amount from the previous value. Because positive amounts (income) are subtracted, income days push the line down and expense days (negative amounts) push the line up. A downward slope means you earned more than you spent over that period; an upward slope means net spending exceeded income.

Pie / Donut Chart — Income vs. Expenses

Endpoint: POST /pi_data
Request body: { "timeZone": "<IANA timezone string>" }
The server iterates over every transaction in the active wallet and accumulates two running totals. Note the counter-intuitive variable naming in the source: positive amounts (money coming in) accumulate into outcome, and negative amounts (money going out) accumulate into income. The JSON response preserves these field names exactly:
@app.route("/pi_data", methods=['POST'])
@login_required
def pi_data():
    data = request.get_json()
    client_tz = pytz.timezone(data.get('timeZone', 'SYSTEM'))

    transfers = MoneyTransfer.query.filter(
        MoneyTransfer.wallet_id == current_user.last_visited_wallet_id
    ).all()

    income = outcome = 0
    for transfer in transfers:
        amount = transfer.amount
        if amount > 0:
            outcome += amount   # positive amounts (credits) → outcome
        else:
            income += amount    # negative amounts (debits) → income

    return jsonify({'income': income, 'outcome': outcome})
The pi_data response field names do not follow the conventional meaning. outcome holds the sum of positive (credit) amounts, and income holds the sum of negative (debit) amounts. This is the behavior as implemented in the source code.
The client renders the result as a donut chart (inner radius ≈ 67% of outer radius) using createPieChart(). Each segment is labeled with its name and value. Segment colors are drawn from a D3 spectral color scale.
function createPieChart(data, width) {
    const arc = d3.arc()
        .innerRadius(radius * 0.67)   // donut hole
        .outerRadius(radius - 1);

    const pie = d3.pie()
        .padAngle(1 / radius)
        .sort(null)
        .value(d => d.value);
    // ...
}

Wallet Scope

Both chart endpoints filter transactions by current_user.last_visited_wallet_id. Switching wallets (see Wallets) and reopening the chart will reflect the newly selected wallet’s data.

Timezone Handling

Both endpoints accept a timeZone field in the POST body. The server uses pytz to convert each transaction’s UTC created_at timestamp into the client’s local date before aggregating. This ensures that a transaction posted at 11 PM in one timezone does not appear on the next calendar day in the chart.
local_date = transfer.created_at.replace(tzinfo=pytz.utc).astimezone(client_tz).date()
The client reads the browser timezone with:
Intl.DateTimeFormat().resolvedOptions().timeZone
// e.g. "Europe/London", "America/Chicago"

Line Chart Data

POST /chart_data — Returns timezone-adjusted daily totals for the active wallet, suitable for plotting a line chart.

Pie Chart Data

POST /pi_data — Returns total income and total expenses across all time for the active wallet.

Build docs developers (and LLMs) love