Fetch account details and display
This commit is contained in:
parent
de04f8d29b
commit
bcfa8f61e1
14
app/urls.py
14
app/urls.py
|
@ -20,7 +20,7 @@ from django.contrib.auth.views import LogoutView
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from two_factor.urls import urlpatterns as tf_urls
|
from two_factor.urls import urlpatterns as tf_urls
|
||||||
|
|
||||||
from core.views import aggregators, base, notifications
|
from core.views import aggregators, banks, base, notifications
|
||||||
|
|
||||||
# from core.views.stripe_callbacks import Callback
|
# from core.views.stripe_callbacks import Callback
|
||||||
|
|
||||||
|
@ -93,4 +93,16 @@ urlpatterns = [
|
||||||
aggregators.ReqInfo.as_view(),
|
aggregators.ReqInfo.as_view(),
|
||||||
name="req_info",
|
name="req_info",
|
||||||
),
|
),
|
||||||
|
# Request bank fetch
|
||||||
|
path(
|
||||||
|
"ops/bank_fetch/<str:pk>/",
|
||||||
|
aggregators.RequestBankFetch.as_view(),
|
||||||
|
name="bank_fetch",
|
||||||
|
),
|
||||||
|
# Bank details by currency
|
||||||
|
path(
|
||||||
|
"banks/<str:type>/details/",
|
||||||
|
banks.BanksCurrencies.as_view(),
|
||||||
|
name="currencies",
|
||||||
|
),
|
||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
|
||||||
|
class AggregatorClient(ABC):
|
||||||
|
def store_account_info(self, account_infos):
|
||||||
|
print("STORE ACCOUNT INFO CALLED")
|
||||||
|
# account_infos = {
|
||||||
|
# bank: accounts
|
||||||
|
# for bank, accounts in account_info.items()
|
||||||
|
# for account in accounts
|
||||||
|
# #if account["account_id"] in self.banks
|
||||||
|
# }
|
||||||
|
# For each bank
|
||||||
|
for bank, accounts in account_infos.items():
|
||||||
|
# Iterate the accounts
|
||||||
|
for index, account in enumerate(list(accounts)):
|
||||||
|
if "account_number" not in account:
|
||||||
|
account_infos[bank][index]["account_number"] = {}
|
||||||
|
fields = ["sort_code", "number", "iban"]
|
||||||
|
for field in fields:
|
||||||
|
if field in account:
|
||||||
|
account_infos[bank][index]["account_number"][
|
||||||
|
field
|
||||||
|
] = account[field]
|
||||||
|
del account_infos[bank][index][field]
|
||||||
|
# if len(account["account_number"]) == 1:
|
||||||
|
# account_infos[bank].remove(account)
|
||||||
|
currencies = [
|
||||||
|
account["currency"]
|
||||||
|
for bank, accounts in account_infos.items()
|
||||||
|
for account in accounts
|
||||||
|
]
|
||||||
|
for bank, accounts in account_infos.items():
|
||||||
|
if not self.instance.account_info:
|
||||||
|
self.instance.account_info = {}
|
||||||
|
self.instance.account_info[bank] = []
|
||||||
|
for account in accounts:
|
||||||
|
self.instance.account_info[bank].append(account)
|
||||||
|
# self.account_info = account_infos
|
||||||
|
self.currencies = currencies
|
||||||
|
|
||||||
|
self.instance.currencies = currencies
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
|
print("CURRENCIES", self.instance.currencies)
|
||||||
|
print("ACCOUNT INFO", self.instance.account_info)
|
|
@ -3,13 +3,14 @@ from datetime import timedelta
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from core.clients.aggregator import AggregatorClient
|
||||||
from core.clients.base import BaseClient
|
from core.clients.base import BaseClient
|
||||||
from core.util import logs
|
from core.util import logs
|
||||||
|
|
||||||
log = logs.get_logger("nordigen")
|
log = logs.get_logger("nordigen")
|
||||||
|
|
||||||
|
|
||||||
class NordigenClient(BaseClient):
|
class NordigenClient(BaseClient, AggregatorClient):
|
||||||
url = "https://ob.nordigen.com/api/v2"
|
url = "https://ob.nordigen.com/api/v2"
|
||||||
|
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
|
@ -60,7 +61,7 @@ class NordigenClient(BaseClient):
|
||||||
"""
|
"""
|
||||||
# This function is a stub.
|
# This function is a stub.
|
||||||
|
|
||||||
return ["GB", "SE"]
|
return ["GB", "SE", "BG", "UA"]
|
||||||
|
|
||||||
async def get_banks(self, country):
|
async def get_banks(self, country):
|
||||||
"""
|
"""
|
||||||
|
@ -128,28 +129,31 @@ class NordigenClient(BaseClient):
|
||||||
|
|
||||||
path = f"accounts/{account_id}/details"
|
path = f"accounts/{account_id}/details"
|
||||||
response = await self.call(path, schema="AccountDetails")
|
response = await self.call(path, schema="AccountDetails")
|
||||||
|
print("RESPONSE", response)
|
||||||
if "account" not in response:
|
if "account" not in response:
|
||||||
return False
|
return False
|
||||||
parsed = response["account"]
|
parsed = response["account"]
|
||||||
if "bban" in parsed and parsed["currency"] == "GBP":
|
if "iban" in parsed and parsed["currency"] == "GBP":
|
||||||
sort_code = parsed["bban"][0:6]
|
if parsed["iban"]:
|
||||||
account_number = parsed["bban"][6:]
|
sort_code = parsed["iban"][-14:-8]
|
||||||
del parsed["bban"]
|
account_number = parsed["iban"][-8:]
|
||||||
if "iban" in parsed:
|
|
||||||
del parsed["iban"]
|
del parsed["iban"]
|
||||||
sort_code = "-".join(list(map("".join, zip(*[iter(sort_code)] * 2))))
|
# if "iban" in parsed:
|
||||||
parsed["sort_code"] = sort_code
|
# del parsed["iban"]
|
||||||
parsed["number"] = account_number
|
sort_code = "-".join(list(map("".join, zip(*[iter(sort_code)] * 2))))
|
||||||
parsed["recipient"] = "TODO"
|
parsed["sort_code"] = sort_code
|
||||||
|
parsed["number"] = account_number
|
||||||
|
parsed["recipient"] = "TODO"
|
||||||
# Let's add the account ID so we can reference it later
|
# Let's add the account ID so we can reference it later
|
||||||
parsed["account_id"] = account_id
|
parsed["account_id"] = account_id
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
async def get_all_account_info(self, requisition=None):
|
async def get_all_account_info(self, requisition=None, store=False):
|
||||||
to_return = {}
|
to_return = {}
|
||||||
if not requisition:
|
if not requisition:
|
||||||
raise NotImplementedError
|
print("NOT REQUISITION")
|
||||||
# requisitions = await self.get_requisitions()
|
requisitions = await self.get_requisitions()
|
||||||
|
print("GOT REQS", requisitions)
|
||||||
else:
|
else:
|
||||||
requisitions = [await self.get_requisition(requisition)]
|
requisitions = [await self.get_requisition(requisition)]
|
||||||
|
|
||||||
|
@ -163,4 +167,8 @@ class NordigenClient(BaseClient):
|
||||||
to_return[req["institution_id"]].append(account_info)
|
to_return[req["institution_id"]].append(account_info)
|
||||||
else:
|
else:
|
||||||
to_return[req["institution_id"]] = [account_info]
|
to_return[req["institution_id"]] = [account_info]
|
||||||
|
|
||||||
|
if store:
|
||||||
|
print("TO STORE IS TRUE")
|
||||||
|
self.store_account_info(to_return)
|
||||||
return to_return
|
return to_return
|
||||||
|
|
|
@ -113,9 +113,12 @@ class AccountDetailsNested(BaseModel):
|
||||||
currency: str
|
currency: str
|
||||||
ownerName: str
|
ownerName: str
|
||||||
cashAccountType: str
|
cashAccountType: str
|
||||||
status: str
|
status: str | None
|
||||||
maskedPan: str | None
|
maskedPan: str | None
|
||||||
details: str
|
details: str | None
|
||||||
|
iban: str | None
|
||||||
|
name: str | None
|
||||||
|
product: str | None
|
||||||
|
|
||||||
|
|
||||||
class AccountDetails(BaseModel):
|
class AccountDetails(BaseModel):
|
||||||
|
|
|
@ -3,6 +3,8 @@ import asyncio
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from core.clients.aggregators.nordigen import NordigenClient
|
||||||
|
from core.models import Aggregator
|
||||||
from core.util import logs
|
from core.util import logs
|
||||||
|
|
||||||
log = logs.get_logger("scheduling")
|
log = logs.get_logger("scheduling")
|
||||||
|
@ -12,6 +14,17 @@ INTERVAL = 5
|
||||||
|
|
||||||
async def job():
|
async def job():
|
||||||
print("Running schedule.")
|
print("Running schedule.")
|
||||||
|
aggregators = Aggregator.objects.filter(enabled=True, fetch_accounts=True)
|
||||||
|
for aggregator in aggregators:
|
||||||
|
if aggregator.service == "nordigen":
|
||||||
|
instance = await NordigenClient(aggregator)
|
||||||
|
print("RUNNING GET ALL ACCOUNT INFO")
|
||||||
|
await instance.get_all_account_info(store=True)
|
||||||
|
print("FINISHED RUNNING")
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f"No such client library: {aggregator.service}")
|
||||||
|
aggregator.fetch_accounts = False
|
||||||
|
aggregator.save()
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-03-09 11:54
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0004_aggregator_access_token_expires'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='aggregator',
|
||||||
|
name='account_info',
|
||||||
|
field=models.JSONField(default=list),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='aggregator',
|
||||||
|
name='currencies',
|
||||||
|
field=models.JSONField(default=list),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-03-09 11:54
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0005_aggregator_account_info_aggregator_currencies'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='aggregator',
|
||||||
|
name='fetch_accounts',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-03-09 14:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0006_aggregator_fetch_accounts'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='aggregator',
|
||||||
|
name='account_info',
|
||||||
|
field=models.JSONField(default=dict),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='aggregator',
|
||||||
|
name='fetch_accounts',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -45,6 +45,11 @@ class Aggregator(models.Model):
|
||||||
access_token_expires = models.DateTimeField(null=True, blank=True)
|
access_token_expires = models.DateTimeField(null=True, blank=True)
|
||||||
poll_interval = models.IntegerField(default=10)
|
poll_interval = models.IntegerField(default=10)
|
||||||
|
|
||||||
|
account_info = models.JSONField(default=dict)
|
||||||
|
currencies = models.JSONField(default=list)
|
||||||
|
|
||||||
|
fetch_accounts = models.BooleanField(default=True)
|
||||||
|
|
||||||
enabled = models.BooleanField(default=True)
|
enabled = models.BooleanField(default=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -219,6 +219,40 @@
|
||||||
Home
|
Home
|
||||||
</a>
|
</a>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
|
<a class="navbar-item" href="#">
|
||||||
|
Profit
|
||||||
|
</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link">
|
||||||
|
Banks
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="{% url 'currencies' type='page' %}">
|
||||||
|
Currencies
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" href="#">
|
||||||
|
Balances
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link">
|
||||||
|
Platforms
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="#">
|
||||||
|
Advert Configuration
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" href="#">
|
||||||
|
Advert Management
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" href="#">
|
||||||
|
Market Research
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
<a class="navbar-link">
|
<a class="navbar-link">
|
||||||
Setup
|
Setup
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% load nsep %}
|
||||||
|
|
||||||
|
{% get_last_invalidation 'core.Aggregator' as last %}
|
||||||
|
{% include 'mixins/partials/notify.html' %}
|
||||||
|
{# cache 600 objects_banks_currencies request.user.id object_list type last #}
|
||||||
|
|
||||||
|
{% for bank, accounts in object_list.items %}
|
||||||
|
<h1 class="title is-4">{{ bank }}</h1>
|
||||||
|
<table
|
||||||
|
class="table is-fullwidth is-hoverable"
|
||||||
|
hx-target="#{{ bank }}-table"
|
||||||
|
id="{{ bank }}-table"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-trigger="{{ context_object_name_singular }}Event from:body"
|
||||||
|
hx-get="{{ list_url }}">
|
||||||
|
<thead>
|
||||||
|
<th>currency</th>
|
||||||
|
<th>owner</th>
|
||||||
|
<th>details</th>
|
||||||
|
<th>payment</th>
|
||||||
|
<th>id</th>
|
||||||
|
|
||||||
|
</thead>
|
||||||
|
{% for account in accounts %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ account.currency }}</td>
|
||||||
|
<td>{{ account.ownerName }}</td>
|
||||||
|
<td>{{ account.details|default_if_none:"—" }}</td>
|
||||||
|
<td>
|
||||||
|
{% for item in account.account_number.values %}
|
||||||
|
<code>{{ item|default_if_none:"—" }}</code>
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
class="has-text-grey"
|
||||||
|
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.account_id }}/');">
|
||||||
|
<span class="icon" data-tooltip="Copy to clipboard">
|
||||||
|
<i class="fa-solid fa-copy" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</table>
|
||||||
|
{% endfor %}
|
||||||
|
{# endcache #}
|
|
@ -1,7 +1,7 @@
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.urls import reverse
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from mixins.views import (
|
from mixins.views import (
|
||||||
ObjectCreate,
|
ObjectCreate,
|
||||||
|
@ -16,25 +16,36 @@ from core.clients.aggregators.nordigen import NordigenClient
|
||||||
from core.forms import AggregatorForm
|
from core.forms import AggregatorForm
|
||||||
from core.models import Aggregator
|
from core.models import Aggregator
|
||||||
from core.util import logs
|
from core.util import logs
|
||||||
|
from core.views.helpers import synchronize_async_helper
|
||||||
|
|
||||||
log = logs.get_logger(__name__)
|
log = logs.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def synchronize_async_helper(to_await):
|
class RequestBankFetch(LoginRequiredMixin, OTPRequiredMixin, View):
|
||||||
async_response = []
|
template_name = "mixins/partials/notify.html"
|
||||||
|
|
||||||
async def run_and_capture_result():
|
def get(self, request, pk=None):
|
||||||
r = await to_await
|
if pk:
|
||||||
async_response.append(r)
|
try:
|
||||||
|
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
||||||
|
aggregators = [aggregator]
|
||||||
|
|
||||||
try:
|
except Aggregator.DoesNotExist:
|
||||||
loop = asyncio.get_event_loop()
|
message = "Aggregator does not exist"
|
||||||
except RuntimeError:
|
context = {
|
||||||
loop = asyncio.new_event_loop()
|
"message": message,
|
||||||
asyncio.set_event_loop(loop)
|
"class": "danger",
|
||||||
coroutine = run_and_capture_result()
|
}
|
||||||
loop.run_until_complete(coroutine)
|
return self.render_to_response(context)
|
||||||
return async_response[0]
|
else:
|
||||||
|
aggregators = Aggregator.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
|
for agg in aggregators:
|
||||||
|
agg.fetch_accounts = True
|
||||||
|
agg.save()
|
||||||
|
|
||||||
|
context = {"class": "success", "message": "Fetch requested"}
|
||||||
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
|
||||||
class ReqsList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
|
class ReqsList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
|
||||||
|
@ -52,7 +63,17 @@ class ReqsList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
|
||||||
|
|
||||||
def get_context_data(self):
|
def get_context_data(self):
|
||||||
context = super().get_context_data()
|
context = super().get_context_data()
|
||||||
context["pk"] = self.kwargs.get("pk")
|
pk = self.kwargs.get("pk")
|
||||||
|
context["pk"] = pk
|
||||||
|
self.extra_buttons = [
|
||||||
|
{
|
||||||
|
"url": reverse("bank_fetch", kwargs={"pk": pk}),
|
||||||
|
"action": "refresh",
|
||||||
|
"method": "get",
|
||||||
|
"label": "Fetch account details",
|
||||||
|
"icon": "fa-solid fa-refresh",
|
||||||
|
},
|
||||||
|
]
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
|
@ -61,18 +82,16 @@ class ReqsList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
|
||||||
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
||||||
|
|
||||||
except Aggregator.DoesNotExist:
|
except Aggregator.DoesNotExist:
|
||||||
message = "Aggregator does not exist"
|
|
||||||
message_class = "danger"
|
|
||||||
context = {
|
context = {
|
||||||
"message": message,
|
"message": "Aggregator does not exist",
|
||||||
"message_class": message_class,
|
"class": "danger",
|
||||||
"window_content": self.window_content,
|
|
||||||
}
|
}
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
self.page_title = (
|
self.page_title = (
|
||||||
f"Requisitions for {aggregator.name} ({aggregator.get_service_display()})"
|
f"Requisitions for {aggregator.name} ({aggregator.get_service_display()})"
|
||||||
)
|
)
|
||||||
|
self.page_subtitle = f"Stored account details: {len(aggregator.currencies)}"
|
||||||
|
|
||||||
run = synchronize_async_helper(NordigenClient(aggregator))
|
run = synchronize_async_helper(NordigenClient(aggregator))
|
||||||
reqs = synchronize_async_helper(run.get_requisitions())
|
reqs = synchronize_async_helper(run.get_requisitions())
|
||||||
|
@ -100,12 +119,9 @@ class AggregatorCountriesList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
|
||||||
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
||||||
|
|
||||||
except Aggregator.DoesNotExist:
|
except Aggregator.DoesNotExist:
|
||||||
message = "Aggregator does not exist"
|
|
||||||
message_class = "danger"
|
|
||||||
context = {
|
context = {
|
||||||
"message": message,
|
"message": "Aggregator does not exist",
|
||||||
"message_class": message_class,
|
"class": "danger",
|
||||||
"window_content": self.window_content,
|
|
||||||
}
|
}
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
@ -141,12 +157,9 @@ class AggregatorCountryBanksList(LoginRequiredMixin, OTPRequiredMixin, ObjectLis
|
||||||
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
||||||
|
|
||||||
except Aggregator.DoesNotExist:
|
except Aggregator.DoesNotExist:
|
||||||
message = "Aggregator does not exist"
|
|
||||||
message_class = "danger"
|
|
||||||
context = {
|
context = {
|
||||||
"message": message,
|
"message": "Aggregator does not exist",
|
||||||
"message_class": message_class,
|
"class": "danger",
|
||||||
"window_content": self.window_content,
|
|
||||||
}
|
}
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
@ -167,12 +180,9 @@ class AggregatorLinkBank(LoginRequiredMixin, OTPRequiredMixin, View):
|
||||||
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
||||||
|
|
||||||
except Aggregator.DoesNotExist:
|
except Aggregator.DoesNotExist:
|
||||||
message = "Aggregator does not exist"
|
|
||||||
message_class = "danger"
|
|
||||||
context = {
|
context = {
|
||||||
"message": message,
|
"message": "Aggregator does not exist",
|
||||||
"message_class": message_class,
|
"class": "danger",
|
||||||
"window_content": self.window_content,
|
|
||||||
}
|
}
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
run = synchronize_async_helper(NordigenClient(aggregator))
|
run = synchronize_async_helper(NordigenClient(aggregator))
|
||||||
|
@ -192,12 +202,9 @@ class ReqDelete(LoginRequiredMixin, OTPRequiredMixin, View):
|
||||||
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
||||||
|
|
||||||
except Aggregator.DoesNotExist:
|
except Aggregator.DoesNotExist:
|
||||||
message = "Aggregator does not exist"
|
|
||||||
message_class = "danger"
|
|
||||||
context = {
|
context = {
|
||||||
"message": message,
|
"message": "Aggregator does not exist",
|
||||||
"message_class": message_class,
|
"class": "danger",
|
||||||
"window_content": self.window_content,
|
|
||||||
}
|
}
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
run = synchronize_async_helper(NordigenClient(aggregator))
|
run = synchronize_async_helper(NordigenClient(aggregator))
|
||||||
|
@ -219,12 +226,9 @@ class ReqInfo(LoginRequiredMixin, OTPRequiredMixin, ObjectRead):
|
||||||
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
aggregator = Aggregator.get_by_id(pk, self.request.user)
|
||||||
|
|
||||||
except Aggregator.DoesNotExist:
|
except Aggregator.DoesNotExist:
|
||||||
message = "Aggregator does not exist"
|
|
||||||
message_class = "danger"
|
|
||||||
context = {
|
context = {
|
||||||
"message": message,
|
"message": "Aggregator does not exist",
|
||||||
"message_class": message_class,
|
"class": "danger",
|
||||||
"window_content": self.window_content,
|
|
||||||
}
|
}
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
run = synchronize_async_helper(NordigenClient(aggregator))
|
run = synchronize_async_helper(NordigenClient(aggregator))
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from mixins.views import ObjectList, ObjectRead
|
||||||
|
from two_factor.views.mixins import OTPRequiredMixin
|
||||||
|
|
||||||
|
from core.models import Aggregator
|
||||||
|
from core.util import logs
|
||||||
|
|
||||||
|
log = logs.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BanksCurrencies(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
|
||||||
|
"""
|
||||||
|
Get a list of configured currencies from the banks we use.
|
||||||
|
"""
|
||||||
|
|
||||||
|
list_template = "partials/banks-currencies-list.html"
|
||||||
|
page_title = "Bank Currencies"
|
||||||
|
|
||||||
|
context_object_name_singular = "currency"
|
||||||
|
context_object_name = "currencies"
|
||||||
|
|
||||||
|
list_url_name = "currencies"
|
||||||
|
list_url_args = ["type"]
|
||||||
|
|
||||||
|
def get_queryset(self, **kwargs):
|
||||||
|
aggregators = Aggregator.objects.filter(user=self.request.user, enabled=True)
|
||||||
|
|
||||||
|
account_info = {}
|
||||||
|
for agg in aggregators:
|
||||||
|
for bank, accounts in agg.account_info.items():
|
||||||
|
if bank not in account_info:
|
||||||
|
account_info[bank] = []
|
||||||
|
for account in accounts:
|
||||||
|
account_info[bank].append(account)
|
||||||
|
|
||||||
|
return account_info
|
||||||
|
|
||||||
|
|
||||||
|
class BankCurrencyDetails(LoginRequiredMixin, OTPRequiredMixin, ObjectRead):
|
||||||
|
"""
|
||||||
|
Get the bank details for the selected currency.
|
||||||
|
"""
|
|
@ -0,0 +1,18 @@
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
|
def synchronize_async_helper(to_await):
|
||||||
|
async_response = []
|
||||||
|
|
||||||
|
async def run_and_capture_result():
|
||||||
|
r = await to_await
|
||||||
|
async_response.append(r)
|
||||||
|
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
coroutine = run_and_capture_result()
|
||||||
|
loop.run_until_complete(coroutine)
|
||||||
|
return async_response[0]
|
Loading…
Reference in New Issue