mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-03-19 07:54:08 +01:00
Merge pull request #184
feat(tools:currency-converter): show 1:1 rates for all available currencies
This commit is contained in:
0
app/apps/mini_tools/utils/__init__.py
Normal file
0
app/apps/mini_tools/utils/__init__.py
Normal file
85
app/apps/mini_tools/utils/exchange_rate_map.py
Normal file
85
app/apps/mini_tools/utils/exchange_rate_map.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from typing import Dict
|
||||
|
||||
from django.db.models import Func, F, Value
|
||||
from django.db.models.functions import Extract
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.currencies.models import ExchangeRate
|
||||
|
||||
|
||||
def get_currency_exchange_map(date=None) -> Dict[str, dict]:
|
||||
"""
|
||||
Creates a nested dictionary of exchange rates and currency information.
|
||||
|
||||
Returns:
|
||||
{
|
||||
'BTC': {
|
||||
'decimal_places': 8,
|
||||
'prefix': '₿',
|
||||
'suffix': '',
|
||||
'rates': {'USD': Decimal('34000.00'), 'EUR': Decimal('31000.00')}
|
||||
},
|
||||
'USD': {
|
||||
'decimal_places': 2,
|
||||
'prefix': '$',
|
||||
'suffix': '',
|
||||
'rates': {'BTC': Decimal('0.0000294'), 'EUR': Decimal('0.91')}
|
||||
},
|
||||
...
|
||||
}
|
||||
"""
|
||||
if date is None:
|
||||
date = timezone.localtime(timezone.now())
|
||||
|
||||
# Get all exchange rates for the closest date
|
||||
exchange_rates = (
|
||||
ExchangeRate.objects.select_related(
|
||||
"from_currency", "to_currency"
|
||||
) # Optimize currency queries
|
||||
.annotate(
|
||||
date_diff=Func(Extract(F("date") - Value(date), "epoch"), function="ABS"),
|
||||
effective_rate=F("rate"),
|
||||
)
|
||||
.order_by("from_currency", "to_currency", "date_diff")
|
||||
.distinct("from_currency", "to_currency")
|
||||
)
|
||||
|
||||
# Initialize the result dictionary
|
||||
rate_map = {}
|
||||
|
||||
# Build the exchange rate mapping with currency info
|
||||
for rate in exchange_rates:
|
||||
# Add from_currency info if not exists
|
||||
if rate.from_currency.name not in rate_map:
|
||||
rate_map[rate.from_currency.name] = {
|
||||
"decimal_places": rate.from_currency.decimal_places,
|
||||
"prefix": rate.from_currency.prefix,
|
||||
"suffix": rate.from_currency.suffix,
|
||||
"rates": {},
|
||||
}
|
||||
|
||||
# Add to_currency info if not exists
|
||||
if rate.to_currency.name not in rate_map:
|
||||
rate_map[rate.to_currency.name] = {
|
||||
"decimal_places": rate.to_currency.decimal_places,
|
||||
"prefix": rate.to_currency.prefix,
|
||||
"suffix": rate.to_currency.suffix,
|
||||
"rates": {},
|
||||
}
|
||||
|
||||
# Add direct rate
|
||||
rate_map[rate.from_currency.name]["rates"][rate.to_currency.name] = {
|
||||
"rate": rate.rate,
|
||||
"decimal_places": rate.to_currency.decimal_places,
|
||||
"prefix": rate.to_currency.prefix,
|
||||
"suffix": rate.to_currency.suffix,
|
||||
}
|
||||
# Add inverse rate
|
||||
rate_map[rate.to_currency.name]["rates"][rate.from_currency.name] = {
|
||||
"rate": 1 / rate.rate,
|
||||
"decimal_places": rate.from_currency.decimal_places,
|
||||
"prefix": rate.from_currency.prefix,
|
||||
"suffix": rate.from_currency.suffix,
|
||||
}
|
||||
|
||||
return rate_map
|
||||
@@ -5,6 +5,7 @@ from apps.common.widgets.decimal import convert_to_decimal
|
||||
from apps.currencies.models import Currency
|
||||
from apps.currencies.utils.convert import convert
|
||||
from apps.mini_tools.forms import CurrencyConverterForm
|
||||
from apps.mini_tools.utils.exchange_rate_map import get_currency_exchange_map
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -14,11 +15,13 @@ def unit_price_calculator(request):
|
||||
|
||||
@login_required
|
||||
def currency_converter(request):
|
||||
rate_map = get_currency_exchange_map()
|
||||
|
||||
form = CurrencyConverterForm()
|
||||
return render(
|
||||
request,
|
||||
"mini_tools/currency_converter/currency_converter.html",
|
||||
context={"form": form},
|
||||
context={"form": form, "rate_map": rate_map},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="card tw-relative h-100 shadow">
|
||||
<div class="tw-absolute tw-h-8 tw-w-8 tw-right-2 tw-top-2 tw-bg-{{ color }}-300 tw-text-{{ color }}-800 text-center align-items-center d-flex justify-content-center rounded-2">
|
||||
<i class="{{ icon }}"></i>
|
||||
{% 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]" {{ attrs }}>{{ title }}{% if help_text %}{% include 'includes/help_icon.html' with content=help_text %}{% endif %}</h5>
|
||||
{{ slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="tw-cursor-pointer text-primary text-end"
|
||||
<div class="tw-cursor-pointer text-primary text-end"
|
||||
_="on click
|
||||
set from_value to #id_from_currency's value
|
||||
set to_value to #id_to_currency's value
|
||||
@@ -58,5 +58,39 @@
|
||||
<i class="fa-solid fa-rotate me-2"></i><span>{% trans 'Invert' %}</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
{% for currency, data in rate_map.items %}
|
||||
<div class="col">
|
||||
<c-ui.info-card color="yellow" title="{{ currency }}">
|
||||
{% for rate in data.rates.values %}
|
||||
<div class="d-flex justify-content-between align-items-baseline mt-2">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">
|
||||
{# <c-amount.display#}
|
||||
{# :amount="1"#}
|
||||
{# :prefix="data.prefix"#}
|
||||
{# :suffix="data.suffix"#}
|
||||
{# :decimal_places="data.decimal_places"></c-amount.display>#}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
{% if currency.income_projected != 0 %}
|
||||
<div class="text-end font-monospace tw-text-green-400">
|
||||
<c-amount.display
|
||||
:amount="rate.rate"
|
||||
:prefix="rate.prefix"
|
||||
:suffix="rate.suffix"
|
||||
:decimal_places="rate.decimal_places"></c-amount.display>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-end font-monospace">-</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</c-ui.info-card>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user