mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-02-25 00:44:52 +01:00
Compare commits
1 Commits
feat/netwo
...
internal_p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d7dd622f5 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -160,3 +160,6 @@ cython_debug/
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/
|
||||
|
||||
postgres_data/
|
||||
.prod.env
|
||||
@@ -126,6 +126,7 @@ To create the first user, open the container's console using Unraid's UI, by cli
|
||||
|
||||
| variable | type | default | explanation |
|
||||
|-------------------------------|-------------|-----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| INTERNAL_PORT | int | 8000 | The port on which the app listens on. Defaults to 8000 if not set. |
|
||||
| DJANGO_ALLOWED_HOSTS | string | localhost 127.0.0.1 | A list of space separated domains and IPs representing the host/domain names that WYGIWYH site can serve. [Click here](https://docs.djangoproject.com/en/5.1/ref/settings/#allowed-hosts) for more details |
|
||||
| HTTPS_ENABLED | true\|false | false | Whether to use secure cookies. If this is set to true, the cookie will be marked as “secure”, which means browsers may ensure that the cookie is only sent under an HTTPS connection |
|
||||
| URL | string | http://localhost http://127.0.0.1 | A list of space separated domains and IPs (with the protocol) representing the trusted origins for unsafe requests (e.g. POST). [Click here](https://docs.djangoproject.com/en/5.1/ref/settings/#csrf-trusted-origins ) for more details |
|
||||
|
||||
@@ -182,29 +182,3 @@ def calculate_historical_account_balance(queryset):
|
||||
historical_account_balance[date_filter(end_date, "b Y")] = month_data
|
||||
|
||||
return historical_account_balance
|
||||
|
||||
|
||||
def calculate_monthly_net_worth_difference(historical_net_worth):
|
||||
diff_dict = OrderedDict()
|
||||
if not historical_net_worth:
|
||||
return diff_dict
|
||||
|
||||
# Get all currencies
|
||||
currencies = set()
|
||||
for data in historical_net_worth.values():
|
||||
currencies.update(data.keys())
|
||||
|
||||
# Initialize prev_values for all currencies
|
||||
prev_values = {currency: Decimal("0.00") for currency in currencies}
|
||||
|
||||
for month, values in historical_net_worth.items():
|
||||
diff_values = {}
|
||||
for currency in sorted(list(currencies)):
|
||||
current_val = values.get(currency, Decimal("0.00"))
|
||||
prev_val = prev_values.get(currency, Decimal("0.00"))
|
||||
diff_values[currency] = current_val - prev_val
|
||||
|
||||
diff_dict[month] = diff_values
|
||||
prev_values = values.copy()
|
||||
|
||||
return diff_dict
|
||||
|
||||
@@ -8,7 +8,6 @@ from django.views.decorators.http import require_http_methods
|
||||
from apps.net_worth.utils.calculate_net_worth import (
|
||||
calculate_historical_currency_net_worth,
|
||||
calculate_historical_account_balance,
|
||||
calculate_monthly_net_worth_difference,
|
||||
)
|
||||
from apps.transactions.models import Transaction
|
||||
from apps.transactions.utils.calculations import (
|
||||
@@ -97,38 +96,6 @@ def net_worth(request):
|
||||
|
||||
chart_data_currency_json = json.dumps(chart_data_currency, cls=DjangoJSONEncoder)
|
||||
|
||||
monthly_difference_data = calculate_monthly_net_worth_difference(
|
||||
historical_net_worth=historical_currency_net_worth
|
||||
)
|
||||
|
||||
diff_labels = (
|
||||
list(monthly_difference_data.keys()) if monthly_difference_data else []
|
||||
)
|
||||
diff_currencies = (
|
||||
list(monthly_difference_data[diff_labels[0]].keys())
|
||||
if monthly_difference_data and diff_labels
|
||||
else []
|
||||
)
|
||||
|
||||
diff_datasets = []
|
||||
for i, currency in enumerate(diff_currencies):
|
||||
data = [
|
||||
float(month_data.get(currency, 0))
|
||||
for month_data in monthly_difference_data.values()
|
||||
]
|
||||
diff_datasets.append(
|
||||
{
|
||||
"label": currency,
|
||||
"data": data,
|
||||
"borderWidth": 3,
|
||||
}
|
||||
)
|
||||
|
||||
chart_data_monthly_difference = {"labels": diff_labels, "datasets": diff_datasets}
|
||||
chart_data_monthly_difference_json = json.dumps(
|
||||
chart_data_monthly_difference, cls=DjangoJSONEncoder
|
||||
)
|
||||
|
||||
historical_account_balance = calculate_historical_account_balance(
|
||||
queryset=transactions_account_queryset
|
||||
)
|
||||
@@ -173,7 +140,6 @@ def net_worth(request):
|
||||
"chart_data_accounts_json": chart_data_accounts_json,
|
||||
"accounts": accounts,
|
||||
"type": view_type,
|
||||
"chart_data_monthly_difference_json": chart_data_monthly_difference_json,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% if icon %}<i class="{{ icon }}"></i>{% else %}<span class="fw-bold">{{ title.0 }}</span>{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="tw:text-{{ color }}-400 fw-bold tw:mr-[50px] {{ title_css_classes }}" {{ attrs }}>{{ title }}{% if help_text %}<c-ui.help-icon :content="help_text" icon=""></c-ui.help-icon>{% endif %}</h5>
|
||||
<h5 class="tw:text-{{ color }}-400 fw-bold tw:mr-[50px]" {{ attrs }}>{{ title }}{% if help_text %}<c-ui.help-icon :content="help_text" icon=""></c-ui.help-icon>{% endif %}</h5>
|
||||
{{ slot }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -354,7 +354,6 @@
|
||||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
format: { maximumFractionDigits: 40, minimumFractionDigits: 1 }
|
||||
}
|
||||
},
|
||||
@@ -370,7 +369,6 @@
|
||||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
format: { maximumFractionDigits: 40, minimumFractionDigits: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,422 +9,338 @@
|
||||
{% block title %}{% if type == "current" %}{% translate 'Current Net Worth' %}{% else %}{% translate 'Projected Net Worth' %}{% endif %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div hx-trigger="every 60m, updated from:window" hx-include="#view-type" class="show-loading" hx-get=""
|
||||
hx-target="body">
|
||||
<div class="h-100 text-center mb-4 pt-2">
|
||||
<div class="btn-group gap-3" role="group" id="view-type" _="on change trigger updated">
|
||||
<input type="radio" class="btn-check"
|
||||
name="view_type"
|
||||
id="current-view"
|
||||
autocomplete="off"
|
||||
value="current"
|
||||
{% if type == "current" %}checked{% endif %}>
|
||||
<label class="btn btn-outline-primary rounded-5" for="current-view"><i
|
||||
class="fa-solid fa-sack-dollar fa-fw me-2"></i>{% trans 'Current' %}</label>
|
||||
<div hx-trigger="every 60m, updated from:window" hx-include="#view-type" class="show-loading" hx-get="" hx-target="body">
|
||||
<div class="h-100 text-center mb-4 pt-2">
|
||||
<div class="btn-group gap-3" role="group" id="view-type" _="on change trigger updated">
|
||||
<input type="radio" class="btn-check"
|
||||
name="view_type"
|
||||
id="current-view"
|
||||
autocomplete="off"
|
||||
value="current"
|
||||
{% if type == "current" %}checked{% endif %}>
|
||||
<label class="btn btn-outline-primary rounded-5" for="current-view"><i
|
||||
class="fa-solid fa-sack-dollar fa-fw me-2"></i>{% trans 'Current' %}</label>
|
||||
|
||||
<input type="radio"
|
||||
class="btn-check"
|
||||
name="view_type"
|
||||
id="projected-view"
|
||||
autocomplete="off"
|
||||
value="projected"
|
||||
{% if type == "projected" %}checked{% endif %}>
|
||||
<label class="btn btn-outline-primary rounded-5" for="projected-view"><i
|
||||
class="fa-solid fa-rocket fa-fw me-2"></i>{% trans 'Projected' %}</label>
|
||||
</div>
|
||||
<input type="radio"
|
||||
class="btn-check"
|
||||
name="view_type"
|
||||
id="projected-view"
|
||||
autocomplete="off"
|
||||
value="projected"
|
||||
{% if type == "projected" %}checked{% endif %}>
|
||||
<label class="btn btn-outline-primary rounded-5" for="projected-view"><i
|
||||
class="fa-solid fa-rocket fa-fw me-2"></i>{% trans 'Projected' %}</label>
|
||||
</div>
|
||||
<div class="container px-md-3 py-3"
|
||||
_="init call initializeAccountChart() then initializeCurrencyChart() then initializeMonthlyDifferenceChart() end">
|
||||
<div class="row gx-xl-4 gy-3 mb-4">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="row row-cols-1 g-4">
|
||||
<div class="col">
|
||||
<c-ui.info-card color="yellow" icon="fa-solid fa-coins" title="{% trans 'By currency' %}"
|
||||
title_css_classes="tw:cursor-pointer"
|
||||
_="on click showAllDatasetsCurrency()">
|
||||
{% for currency in currency_net_worth.values %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="currency-name text-start font-monospace tw:text-gray-300 tw:cursor-pointer"
|
||||
_="on click showOnlyCurrencyDataset('{{ currency.currency.name }}')">
|
||||
{{ currency.currency.name }}
|
||||
</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div>
|
||||
<c-amount.display
|
||||
:amount="currency.total_final"
|
||||
:prefix="currency.currency.prefix"
|
||||
:suffix="currency.currency.suffix"
|
||||
:decimal_places="currency.currency.decimal_places"
|
||||
color="{% if currency.total_final > 0 %}green{% elif currency.total_final < 0 %}red{% endif %}"
|
||||
text-end></c-amount.display>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container px-md-3 py-3" _="init call initializeAccountChart() then initializeCurrencyChart() end">
|
||||
<div class="row gx-xl-4 gy-3 mb-4">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="row row-cols-1 g-4">
|
||||
<div class="col">
|
||||
<c-ui.info-card color="yellow" icon="fa-solid fa-coins" title="{% trans 'By currency' %}"
|
||||
_="on click showAllDatasetsCurrency()">
|
||||
{% for currency in currency_net_worth.values %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="currency-name text-start font-monospace tw:text-gray-300"
|
||||
_="on click showOnlyCurrencyDataset('{{ currency.currency.name }}')">
|
||||
{{ currency.currency.name }}
|
||||
</div>
|
||||
</div>
|
||||
{% if currency.exchanged and currency.exchanged.total_final %}
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div>
|
||||
<c-amount.display
|
||||
:amount="currency.exchanged.total_final"
|
||||
:prefix="currency.exchanged.currency.prefix"
|
||||
:suffix="currency.exchanged.currency.suffix"
|
||||
:decimal_places="currency.exchanged.currency.decimal_places"
|
||||
text-end
|
||||
color="grey"></c-amount.display>
|
||||
:amount="currency.total_final"
|
||||
:prefix="currency.currency.prefix"
|
||||
:suffix="currency.currency.suffix"
|
||||
:decimal_places="currency.currency.decimal_places"
|
||||
color="{% if currency.total_final > 0 %}green{% elif currency.total_final < 0 %}red{% endif %}"
|
||||
text-end></c-amount.display>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if currency.consolidated and currency.consolidated.total_final != currency.total_final %}
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="account-name text-start font-monospace tw:text-gray-300">
|
||||
<span class="hierarchy-line-icon"></span>{% trans 'Consolidated' %}</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div class="">
|
||||
<c-amount.display
|
||||
</div>
|
||||
</div>
|
||||
{% if currency.exchanged and currency.exchanged.total_final %}
|
||||
<div>
|
||||
<c-amount.display
|
||||
:amount="currency.exchanged.total_final"
|
||||
:prefix="currency.exchanged.currency.prefix"
|
||||
:suffix="currency.exchanged.currency.suffix"
|
||||
:decimal_places="currency.exchanged.currency.decimal_places"
|
||||
text-end
|
||||
color="grey"></c-amount.display>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if currency.consolidated and currency.consolidated.total_final != currency.total_final %}
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="account-name text-start font-monospace tw:text-gray-300">
|
||||
<span class="hierarchy-line-icon"></span>{% trans 'Consolidated' %}</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div class="">
|
||||
<c-amount.display
|
||||
:amount="currency.consolidated.total_final"
|
||||
:prefix="currency.consolidated.currency.prefix"
|
||||
:suffix="currency.consolidated.currency.suffix"
|
||||
:decimal_places="currency.consolidated.currency.decimal_places"
|
||||
color="{% if currency.consolidated.total_final > 0 %}green{% elif currency.consolidated.total_final < 0 %}red{% endif %}"
|
||||
text-end></c-amount.display>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</c-ui.info-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-7">
|
||||
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="evolution-tab" data-bs-toggle="tab"
|
||||
data-bs-target="#evolution-tab-pane" type="button" role="tab" aria-controls="evolution-tab-pane"
|
||||
aria-selected="true">{% trans 'Evolution' %}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="diff-tab" data-bs-toggle="tab" data-bs-target="#diff-tab-pane" type="button"
|
||||
role="tab" aria-controls="diff-tab-pane" aria-selected="false">{% trans 'Difference' %}</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-pane fade show active" id="evolution-tab-pane" role="tabpanel"
|
||||
aria-labelledby="evolution-tab" tabindex="0">
|
||||
<div class="chart-container position-relative tw:min-h-[40vh] tw:h-full tw:w-full">
|
||||
<canvas id="currencyBalanceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="diff-tab-pane" role="tabpanel" aria-labelledby="diff-tab" tabindex="0">
|
||||
<div class="chart-container position-relative tw:min-h-[40vh] tw:h-full tw:w-full">
|
||||
<canvas id="monthlyDifferenceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</c-ui.info-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row gx-xl-4 gy-3 mt-4">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="row row-cols-1 g-4">
|
||||
<div class="col">
|
||||
<c-ui.info-card color="blue" icon="fa-solid fa-wallet" title="{% trans 'By account' %}"
|
||||
title_css_classes="tw:cursor-pointer"
|
||||
_="on click showAllDatasetsAccount()">
|
||||
{% regroup account_net_worth.values by account.group as account_data %}
|
||||
{% for data in account_data %}
|
||||
{% if data.grouper %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="text-start font-monospace tw:text-gray-300"><span class="badge text-bg-primary">
|
||||
{{ data.grouper }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
{% for account in data.list %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="account-name text-start font-monospace tw:text-gray-300 tw:cursor-pointer"
|
||||
_="on click showOnlyAccountDataset('{{ account.account.name }}')">
|
||||
<span class="hierarchy-line-icon"></span>{{ account.account.name }}</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div class="">
|
||||
<c-amount.display
|
||||
:amount="account.total_final"
|
||||
:prefix="account.currency.prefix"
|
||||
:suffix="account.currency.suffix"
|
||||
:decimal_places="account.currency.decimal_places"
|
||||
color="{% if account.total_final > 0 %}green{% elif account.total_final < 0 %}red{% endif %}"></c-amount.display>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if account.exchanged and account.exchanged.total_final %}
|
||||
<c-amount.display
|
||||
:amount="account.exchanged.total_final"
|
||||
:prefix="account.exchanged.currency.prefix"
|
||||
:suffix="account.exchanged.currency.suffix"
|
||||
:decimal_places="account.exchanged.currency.decimal_places"
|
||||
color="grey"
|
||||
text-end></c-amount.display>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for account in data.list %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="account-name text-start font-monospace tw:text-gray-300 tw:cursor-pointer"
|
||||
_="on click showOnlyAccountDataset('{{ account.account.name }}')">
|
||||
{{ account.account.name }}
|
||||
</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div>
|
||||
<c-amount.display
|
||||
:amount="account.total_final"
|
||||
:prefix="account.currency.prefix"
|
||||
:suffix="account.currency.suffix"
|
||||
:decimal_places="account.currency.decimal_places"
|
||||
color="{% if account.total_final > 0 %}green{% elif account.total_final < 0 %}red{% endif %}"></c-amount.display>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if account.exchanged and account.exchanged.total_final %}
|
||||
<c-amount.display
|
||||
:amount="account.exchanged.total_final"
|
||||
:prefix="account.exchanged.currency.prefix"
|
||||
:suffix="account.exchanged.currency.suffix"
|
||||
:decimal_places="account.exchanged.currency.decimal_places"
|
||||
color="grey"
|
||||
text-end></c-amount.display>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</c-ui.info-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-7">
|
||||
<div class="chart-container position-relative tw:min-h-[40vh] tw:h-full">
|
||||
<canvas id="accountBalanceChart"></canvas>
|
||||
</div>
|
||||
<div class="col-12 col-xl-7">
|
||||
<div class="chart-container position-relative tw:min-h-[40vh] tw:h-full">
|
||||
<canvas id="currencyBalanceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row gx-xl-4 gy-3 mt-4">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="row row-cols-1 g-4">
|
||||
<div class="col">
|
||||
<c-ui.info-card color="blue" icon="fa-solid fa-wallet" title="{% trans 'By account' %}"
|
||||
_="on click showAllDatasetsAccount()">
|
||||
{% regroup account_net_worth.values by account.group as account_data %}
|
||||
{% for data in account_data %}
|
||||
{% if data.grouper %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="text-start font-monospace tw:text-gray-300"><span class="badge text-bg-primary">
|
||||
{{ data.grouper }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
{% for account in data.list %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="account-name text-start font-monospace tw:text-gray-300"
|
||||
_="on click showOnlyAccountDataset('{{ account.account.name }}')">
|
||||
<span class="hierarchy-line-icon"></span>{{ account.account.name }}</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div class="">
|
||||
<c-amount.display
|
||||
:amount="account.total_final"
|
||||
:prefix="account.currency.prefix"
|
||||
:suffix="account.currency.suffix"
|
||||
:decimal_places="account.currency.decimal_places"
|
||||
color="{% if account.total_final > 0 %}green{% elif account.total_final < 0 %}red{% endif %}"></c-amount.display>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if account.exchanged and account.exchanged.total_final %}
|
||||
<c-amount.display
|
||||
:amount="account.exchanged.total_final"
|
||||
:prefix="account.exchanged.currency.prefix"
|
||||
:suffix="account.exchanged.currency.suffix"
|
||||
:decimal_places="account.exchanged.currency.decimal_places"
|
||||
color="grey"
|
||||
text-end></c-amount.display>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for account in data.list %}
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<div class="d-flex align-items-baseline w-100">
|
||||
<div class="account-name text-start font-monospace tw:text-gray-300"
|
||||
_="on click showOnlyAccountDataset('{{ account.account.name }}')">
|
||||
{{ account.account.name }}
|
||||
</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
<div>
|
||||
<c-amount.display
|
||||
:amount="account.total_final"
|
||||
:prefix="account.currency.prefix"
|
||||
:suffix="account.currency.suffix"
|
||||
:decimal_places="account.currency.decimal_places"
|
||||
color="{% if account.total_final > 0 %}green{% elif account.total_final < 0 %}red{% endif %}"></c-amount.display>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if account.exchanged and account.exchanged.total_final %}
|
||||
<c-amount.display
|
||||
:amount="account.exchanged.total_final"
|
||||
:prefix="account.exchanged.currency.prefix"
|
||||
:suffix="account.exchanged.currency.suffix"
|
||||
:decimal_places="account.exchanged.currency.decimal_places"
|
||||
color="grey"
|
||||
text-end></c-amount.display>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</c-ui.info-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-7">
|
||||
<div class="chart-container position-relative tw:min-h-[40vh] tw:h-full">
|
||||
<canvas id="accountBalanceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var currencyChart;
|
||||
|
||||
function initializeCurrencyChart() {
|
||||
// Destroy existing chart if it exists
|
||||
if (currencyChart) {
|
||||
currencyChart.destroy();
|
||||
}
|
||||
|
||||
var chartData = JSON.parse('{{ chart_data_currency_json|safe }}');
|
||||
var currencies = {{ currencies|safe }};
|
||||
var ctx = document.getElementById('currencyBalanceChart').getContext('2d');
|
||||
|
||||
currencyChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: chartData,
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '{% translate 'Evolution by currency' %}'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
...Object.fromEntries(currencies.map((currency, i) => [
|
||||
`y${i}`,
|
||||
{
|
||||
type: 'linear',
|
||||
display: true,
|
||||
grid: {
|
||||
drawOnChartArea: i === 0,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
format: {maximumFractionDigits: 40, minimumFractionDigits: 0}
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
]))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="accountBalanceChartScript">
|
||||
var accountChart;
|
||||
|
||||
function initializeAccountChart() {
|
||||
// Destroy existing chart if it exists
|
||||
if (accountChart) {
|
||||
accountChart.destroy();
|
||||
}
|
||||
|
||||
var chartData = JSON.parse('{{ chart_data_accounts_json|safe }}');
|
||||
var accounts = {{ accounts|safe }};
|
||||
var ctx = document.getElementById('accountBalanceChart').getContext('2d');
|
||||
|
||||
accountChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: chartData,
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
stacked: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '{% translate "Evolution by account" %}'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
...Object.fromEntries(accounts.map((account, i) => [
|
||||
`y-axis-${i}`,
|
||||
{
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: i % 2 === 0 ? 'left' : 'right',
|
||||
grid: {
|
||||
drawOnChartArea: i === 0,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
format: {maximumFractionDigits: 40, minimumFractionDigits: 0}
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
]))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="monthlyDifferenceChartScript">
|
||||
var monthlyDifferenceChart;
|
||||
|
||||
function initializeMonthlyDifferenceChart() {
|
||||
if (monthlyDifferenceChart) {
|
||||
monthlyDifferenceChart.destroy();
|
||||
}
|
||||
|
||||
var chartData = JSON.parse('{{ chart_data_monthly_difference_json|safe }}');
|
||||
var ctx = document.getElementById('monthlyDifferenceChart').getContext('2d');
|
||||
|
||||
monthlyDifferenceChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '{% translate "Difference" %}'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
ticks: {
|
||||
display: false,
|
||||
format: {maximumFractionDigits: 40, minimumFractionDigits: 0}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/hyperscript">
|
||||
def showOnlyAccountDataset(datasetName)
|
||||
for dataset in accountChart.data.datasets
|
||||
set isMatch to dataset.label is datasetName
|
||||
call accountChart.setDatasetVisibility(accountChart.data.datasets.indexOf(dataset), isMatch)
|
||||
end
|
||||
call accountChart.update()
|
||||
end
|
||||
|
||||
def showOnlyCurrencyDataset(datasetName)
|
||||
for dataset in currencyChart.data.datasets
|
||||
set isMatch to dataset.label is datasetName
|
||||
call currencyChart.setDatasetVisibility(currencyChart.data.datasets.indexOf(dataset), isMatch)
|
||||
end
|
||||
call currencyChart.update()
|
||||
|
||||
for dataset in monthlyDifferenceChart.data.datasets
|
||||
set isMatch to dataset.label is datasetName
|
||||
call monthlyDifferenceChart.setDatasetVisibility(monthlyDifferenceChart.data.datasets.indexOf(dataset), isMatch)
|
||||
end
|
||||
call monthlyDifferenceChart.update()
|
||||
end
|
||||
|
||||
def showAllDatasetsAccount()
|
||||
for dataset in accountChart.data.datasets
|
||||
call accountChart.setDatasetVisibility(accountChart.data.datasets.indexOf(dataset), true)
|
||||
end
|
||||
call accountChart.update()
|
||||
end
|
||||
|
||||
def showAllDatasetsCurrency()
|
||||
for dataset in currencyChart.data.datasets
|
||||
call currencyChart.setDatasetVisibility(currencyChart.data.datasets.indexOf(dataset), true)
|
||||
end
|
||||
call currencyChart.update()
|
||||
|
||||
for dataset in monthlyDifferenceChart.data.datasets
|
||||
call monthlyDifferenceChart.setDatasetVisibility(monthlyDifferenceChart.data.datasets.indexOf(dataset), true)
|
||||
end
|
||||
call monthlyDifferenceChart.update()
|
||||
end
|
||||
</script>
|
||||
</div>
|
||||
<c-ui.transactions_fab></c-ui.transactions_fab>
|
||||
|
||||
<script>
|
||||
var currencyChart;
|
||||
|
||||
function initializeCurrencyChart() {
|
||||
// Destroy existing chart if it exists
|
||||
if (currencyChart) {
|
||||
currencyChart.destroy();
|
||||
}
|
||||
|
||||
var chartData = JSON.parse('{{ chart_data_currency_json|safe }}');
|
||||
var currencies = {{ currencies|safe }};
|
||||
var ctx = document.getElementById('currencyBalanceChart').getContext('2d');
|
||||
|
||||
currencyChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: chartData,
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '{% translate 'Evolution by currency' %}'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
...Object.fromEntries(currencies.map((currency, i) => [
|
||||
`y${i}`,
|
||||
{
|
||||
type: 'linear',
|
||||
display: true,
|
||||
grid: {
|
||||
drawOnChartArea: i === 0,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
format: {maximumFractionDigits: 40, minimumFractionDigits: 0}
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
]))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="accountBalanceChartScript">
|
||||
var accountChart;
|
||||
|
||||
function initializeAccountChart() {
|
||||
// Destroy existing chart if it exists
|
||||
if (accountChart) {
|
||||
accountChart.destroy();
|
||||
}
|
||||
|
||||
var chartData = JSON.parse('{{ chart_data_accounts_json|safe }}');
|
||||
var accounts = {{ accounts|safe }};
|
||||
var ctx = document.getElementById('accountBalanceChart').getContext('2d');
|
||||
|
||||
accountChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: chartData,
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
stacked: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '{% translate "Evolution by account" %}'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
...Object.fromEntries(accounts.map((account, i) => [
|
||||
`y-axis-${i}`,
|
||||
{
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: i % 2 === 0 ? 'left' : 'right',
|
||||
grid: {
|
||||
drawOnChartArea: i === 0,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
format: {maximumFractionDigits: 40, minimumFractionDigits: 0}
|
||||
},
|
||||
border: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
]))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/hyperscript">
|
||||
def showOnlyAccountDataset(datasetName)
|
||||
for dataset in accountChart.data.datasets
|
||||
set isMatch to dataset.label is datasetName
|
||||
call accountChart.setDatasetVisibility(accountChart.data.datasets.indexOf(dataset), isMatch)
|
||||
end
|
||||
call accountChart.update()
|
||||
end
|
||||
|
||||
def showOnlyCurrencyDataset(datasetName)
|
||||
for dataset in currencyChart.data.datasets
|
||||
set isMatch to dataset.label is datasetName
|
||||
call currencyChart.setDatasetVisibility(currencyChart.data.datasets.indexOf(dataset), isMatch)
|
||||
end
|
||||
call currencyChart.update()
|
||||
end
|
||||
|
||||
def showAllDatasetsAccount()
|
||||
for dataset in accountChart.data.datasets
|
||||
call accountChart.setDatasetVisibility(accountChart.data.datasets.indexOf(dataset), true)
|
||||
end
|
||||
call accountChart.update()
|
||||
end
|
||||
|
||||
def showAllDatasetsCurrency()
|
||||
for dataset in currencyChart.data.datasets
|
||||
call currencyChart.setDatasetVisibility(currencyChart.data.datasets.indexOf(dataset), true)
|
||||
end
|
||||
call currencyChart.update()
|
||||
end
|
||||
</script>
|
||||
</div>
|
||||
<c-ui.transactions_fab></c-ui.transactions_fab>
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% block content %}
|
||||
<div>
|
||||
<div class="container">
|
||||
<div class="row tw:h-dvh d-flex justify-content-center align-items-center">
|
||||
<div class="row vh-100 d-flex justify-content-center align-items-center">
|
||||
<div class="col-md-6 col-xl-4 col-12">
|
||||
{% settings "DEMO" as demo_mode %}
|
||||
{% if demo_mode %}
|
||||
|
||||
@@ -15,7 +15,7 @@ services:
|
||||
- ./frontend/:/usr/src/frontend:z
|
||||
- wygiwyh_temp:/usr/src/app/temp/
|
||||
ports:
|
||||
- "${OUTBOUND_PORT}:8000"
|
||||
- "${OUTBOUND_PORT:-8000}:${INTERNAL_PORT:-8000}"
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
|
||||
@@ -4,7 +4,7 @@ services:
|
||||
container_name: ${SERVER_NAME}
|
||||
command: /start-single
|
||||
ports:
|
||||
- "${OUTBOUND_PORT}:8000"
|
||||
- "${OUTBOUND_PORT:-8000}:${INTERNAL_PORT:-8000}"
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
|
||||
@@ -4,6 +4,9 @@ set -o errexit
|
||||
set -o pipefail
|
||||
set -o nounset
|
||||
|
||||
# Set INTERNAL_PORT with default value of 8000
|
||||
INTERNAL_PORT=${INTERNAL_PORT:-8000}
|
||||
|
||||
rm -f /tmp/migrations_complete
|
||||
|
||||
python manage.py migrate
|
||||
@@ -13,4 +16,4 @@ touch /tmp/migrations_complete
|
||||
|
||||
python manage.py setup_users
|
||||
|
||||
exec python manage.py runserver 0.0.0.0:8000
|
||||
exec python manage.py runserver 0.0.0.0:$INTERNAL_PORT
|
||||
|
||||
@@ -4,6 +4,9 @@ set -o errexit
|
||||
set -o pipefail
|
||||
set -o nounset
|
||||
|
||||
# Set INTERNAL_PORT with default value of 8000
|
||||
INTERNAL_PORT=${INTERNAL_PORT:-8000}
|
||||
|
||||
# Remove flag file if it exists from previous run
|
||||
rm -f /tmp/migrations_complete
|
||||
|
||||
@@ -15,4 +18,4 @@ touch /tmp/migrations_complete
|
||||
|
||||
python manage.py setup_users
|
||||
|
||||
exec gunicorn WYGIWYH.wsgi:application --bind 0.0.0.0:8000 --timeout 600
|
||||
exec gunicorn WYGIWYH.wsgi:application --bind 0.0.0.0:$INTERNAL_PORT --timeout 600
|
||||
|
||||
Reference in New Issue
Block a user