Compare commits

...

2 Commits

Author SHA1 Message Date
1e85e830b2
Fix positions CRUD 2022-12-08 07:20:07 +00:00
d396abca84
Use CRUD helper for position list 2022-12-08 07:20:07 +00:00
3 changed files with 48 additions and 72 deletions

View File

@ -17,7 +17,7 @@
<th>trades</th> <th>trades</th>
<th>actions</th> <th>actions</th>
</thead> </thead>
{% for item in items %} {% for item in object_list %}
<tr class=" <tr class="
{% if item.unrealized_pl > 0 %}has-background-success-light {% if item.unrealized_pl > 0 %}has-background-success-light
{% elif item.unrealized_pl < 0 %}has-background-danger-light {% elif item.unrealized_pl < 0 %}has-background-danger-light

View File

@ -33,7 +33,7 @@ class RestrictedViewMixin:
page_kwarg = "page" page_kwarg = "page"
ordering = None ordering = None
def get_queryset(self): def get_queryset(self, **kwargs):
""" """
This function is overriden to filter the objects by the requesting user. This function is overriden to filter the objects by the requesting user.
""" """
@ -70,14 +70,18 @@ class RestrictedViewMixin:
class ObjectNameMixin(object): class ObjectNameMixin(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if self.model is None:
self.title = self.context_object_name.title()
self.title_singular = self.context_object_name_singular.title()
else:
self.title_singular = self.model._meta.verbose_name.title() # Hook self.title_singular = self.model._meta.verbose_name.title() # Hook
self.context_object_name_singular = self.title_singular.lower() # hook self.context_object_name_singular = self.title_singular.lower() # hook
self.title = self.model._meta.verbose_name_plural.title() # Hooks self.title = self.model._meta.verbose_name_plural.title() # Hooks
self.context_object_name = self.title.lower() # hooks self.context_object_name = self.title.lower() # hooks
self.context_object_name = self.context_object_name.replace(" ", "") self.context_object_name = self.context_object_name.replace(" ", "")
self.context_object_name_singular = self.context_object_name_singular.replace( self.context_object_name_singular = (
" ", "" self.context_object_name_singular.replace(" ", "")
) )
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -97,11 +101,12 @@ class ObjectList(RestrictedViewMixin, ObjectNameMixin, ListView):
submit_url_name = None submit_url_name = None
delete_all_url_name = None delete_all_url_name = None
widget_options = None
# copied from BaseListView # copied from BaseListView
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.request = request self.request = request
self.object_list = self.get_queryset() self.object_list = self.get_queryset(**kwargs)
allow_empty = self.get_allow_empty() allow_empty = self.get_allow_empty()
type = kwargs.get("type", None) type = kwargs.get("type", None)
@ -114,6 +119,7 @@ class ObjectList(RestrictedViewMixin, ObjectNameMixin, ListView):
list_url_args = {} list_url_args = {}
for arg in self.list_url_args: for arg in self.list_url_args:
if arg in locals():
list_url_args[arg] = locals()[arg] list_url_args[arg] = locals()[arg]
orig_type = type orig_type = type
@ -132,9 +138,7 @@ class ObjectList(RestrictedViewMixin, ObjectNameMixin, ListView):
is_empty = not self.object_list is_empty = not self.object_list
if is_empty: if is_empty:
raise Http404("Empty list") raise Http404("Empty list")
submit_url = reverse(self.submit_url_name, kwargs={"type": type})
list_url = reverse(self.list_url_name, kwargs=list_url_args)
context = self.get_context_data() context = self.get_context_data()
context["title"] = self.title + f" ({type})" context["title"] = self.title + f" ({type})"
context["title_singular"] = self.title_singular context["title_singular"] = self.title_singular
@ -144,12 +148,19 @@ class ObjectList(RestrictedViewMixin, ObjectNameMixin, ListView):
context["page_title"] = self.page_title context["page_title"] = self.page_title
context["page_subtitle"] = self.page_subtitle context["page_subtitle"] = self.page_subtitle
context["type"] = type context["type"] = type
context["submit_url"] = submit_url
context["list_url"] = list_url
context["context_object_name"] = self.context_object_name context["context_object_name"] = self.context_object_name
context["context_object_name_singular"] = self.context_object_name_singular context["context_object_name_singular"] = self.context_object_name_singular
if self.submit_url_name is not None:
context["submit_url"] = reverse(self.submit_url_name, kwargs={"type": type})
if self.list_url_name is not None:
context["list_url"] = reverse(self.list_url_name, kwargs=list_url_args)
if self.delete_all_url_name: if self.delete_all_url_name:
context["delete_all_url"] = reverse(self.delete_all_url_name) context["delete_all_url"] = reverse(self.delete_all_url_name)
if self.widget_options:
context["widget_options"] = self.widget_options
# Return partials for HTMX # Return partials for HTMX
if self.request.htmx: if self.request.htmx:

View File

@ -3,7 +3,6 @@ import uuid
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseBadRequest from django.http import HttpResponseBadRequest
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse
from django.views import View from django.views import View
from rest_framework.parsers import FormParser from rest_framework.parsers import FormParser
from two_factor.views.mixins import OTPRequiredMixin from two_factor.views.mixins import OTPRequiredMixin
@ -11,24 +10,11 @@ from two_factor.views.mixins import OTPRequiredMixin
from core.exchanges import GenericAPIError from core.exchanges import GenericAPIError
from core.models import Account, Trade from core.models import Account, Trade
from core.util import logs from core.util import logs
from core.views import ObjectList
log = logs.get_logger(__name__) log = logs.get_logger(__name__)
def get_positions(user, account_id=None):
items = []
accounts = Account.objects.filter(user=user)
for account in accounts:
try:
positions = account.client.get_all_positions()
except GenericAPIError:
continue
for item in positions:
items.append(item)
return items
def annotate_positions(positions, user, return_order_ids=False): def annotate_positions(positions, user, return_order_ids=False):
""" """
Annotate positions with trade information. Annotate positions with trade information.
@ -55,55 +41,34 @@ def annotate_positions(positions, user, return_order_ids=False):
pass pass
class Positions(LoginRequiredMixin, OTPRequiredMixin, View): class Positions(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
allowed_types = ["modal", "widget", "window", "page"]
window_content = "window-content/objects.html"
list_template = "partials/position-list.html" list_template = "partials/position-list.html"
page_title = "Live positions from all exchanges" page_title = "Live positions from all exchanges"
page_subtitle = "Manual trades are editable under 'Bot Trades' tab." page_subtitle = "Manual trades are editable under 'Bot Trades' tab."
context_object_name_singular = "position" context_object_name_singular = "position"
context_object_name = "positions" context_object_name = "positions"
def get(self, request, type, account_id=None): list_url_name = "positions"
if type not in self.allowed_types: list_url_args = ["type", "account_id"]
return HttpResponseBadRequest
self.template_name = f"wm/{type}.html"
unique = str(uuid.uuid4())[:8]
items = get_positions(request.user, account_id)
annotate_positions(items, request.user, return_order_ids=False)
orig_type = type widget_options = 'gs-w="12" gs-h="1" gs-y="0" gs-x="0"'
if type == "page":
type = "modal" def get_queryset(self, **kwargs):
cast = { account_id = kwargs.get("account_id", None)
"type": orig_type,
}
if account_id: if account_id:
cast["account_id"] = account_id self.extra_context["account_id"] = account_id
list_url = reverse("positions", kwargs={**cast}) items = []
context = { accounts = Account.objects.filter(user=self.request.user)
"title": f"Positions ({type})", for account in accounts:
"unique": unique, try:
"window_content": self.window_content, positions = account.client.get_all_positions()
"list_template": self.list_template, except GenericAPIError:
"items": items, continue
"type": type,
"page_title": self.page_title, for item in positions:
"page_subtitle": self.page_subtitle, items.append(item)
"list_url": list_url, annotate_positions(items, self.request.user, return_order_ids=False)
"context_object_name_singular": self.context_object_name_singular, return items
"context_object_name": self.context_object_name,
"widget_options": 'gs-w="12" gs-h="1" gs-y="0" gs-x="0"',
}
# Return partials for HTMX
if self.request.htmx:
if request.headers["HX-Target"] == self.context_object_name + "-table":
self.template_name = self.list_template
elif orig_type == "page":
self.template_name = self.list_template
else:
context["window_content"] = self.list_template
return render(request, self.template_name, context)
class PositionAction(LoginRequiredMixin, OTPRequiredMixin, View): class PositionAction(LoginRequiredMixin, OTPRequiredMixin, View):