Show trades

This commit is contained in:
Mark Veidemanis 2023-03-10 00:23:05 +00:00
parent 1e7d8f6c8d
commit fa7ea66c65
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
12 changed files with 236 additions and 16 deletions

View File

@ -143,4 +143,10 @@ urlpatterns = [
platforms.PlatformDelete.as_view(), platforms.PlatformDelete.as_view(),
name="platform_delete", name="platform_delete",
), ),
# Trades
path(
"trades/<str:type>/",
platforms.PlatformTrades.as_view(),
name="trades",
),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

View File

@ -28,6 +28,15 @@ class LocalPlatformClient(ABC):
async def connect(self): async def connect(self):
self.api = AgoraDesk(self.instance.token) self.api = AgoraDesk(self.instance.token)
async def call_method(self, method, *args, **kwargs):
"""
Call a method using the self.api object.
"""
if hasattr(self.api, method):
return await getattr(self.api, method)(*args, **kwargs)
else:
raise Exception(f"Method {method} not found in {self.name} API.")
# TODO: do in schedules # TODO: do in schedules
# def setup_loop(self): # def setup_loop(self):
# """ # """
@ -62,19 +71,18 @@ class LocalPlatformClient(ABC):
async def wrap_dashboard(self, dash=None): # backwards compatibility with TX async def wrap_dashboard(self, dash=None): # backwards compatibility with TX
if not dash: if not dash:
dash = await self.api.dashboard() # dash = await self.api.dashboard()
dash = await self.call("dashboard")
print("DASH22", dash)
# if dash["response"] is None: # if dash["response"] is None:
# return False # return False
dash_tmp = {} dash_tmp = {}
if not dash: if not dash:
return False return False
if not dash["response"]:
return False if dash["contact_count"] > 0:
if "data" not in dash["response"]: for contact in dash["contact_list"]:
# log.error(f"Data not in dashboard response: {dash}")
return dash_tmp
if dash["response"]["data"]["contact_count"] > 0:
for contact in dash["response"]["data"]["contact_list"]:
contact_id = contact["data"]["contact_id"] contact_id = contact["data"]["contact_id"]
dash_tmp[contact_id] = contact dash_tmp[contact_id] = contact
return dash_tmp return dash_tmp

View File

@ -6,7 +6,7 @@ from core.clients.platform import LocalPlatformClient
from core.lib.money import Money from core.lib.money import Money
class AgoraClient(BaseClient, LocalPlatformClient): class AgoraClient(LocalPlatformClient, BaseClient):
""" """
AgoraDesk API handler. AgoraDesk API handler.
""" """

View File

@ -87,6 +87,7 @@ class AgoraDesk:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.post(api_call_url, **cast) as response_raw: async with session.post(api_call_url, **cast) as response_raw:
response = await response_raw.json() response = await response_raw.json()
status_code = response_raw.status
else: else:
# response = httpx.get(url=api_call_url, headers=headers, params=query_values) # response = httpx.get(url=api_call_url, headers=headers, params=query_values)
@ -95,12 +96,15 @@ class AgoraDesk:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(api_call_url, **cast) as response_raw: async with session.get(api_call_url, **cast) as response_raw:
response = await response_raw.json() response = await response_raw.json()
status_code = response_raw.status
if response: if response:
print("YES RESPONSE", response)
logger.debug(response) logger.debug(response)
result["status"] = response.code result["status"] = status_code
if response.code == 200: if status_code == 200:
result["success"] = True result["success"] = True
result["message"] = "OK" result["message"] = "OK"
result["response"] = response
else: else:
result["message"] = "API ERROR" result["message"] = "API ERROR"

View File

@ -107,5 +107,4 @@ class AntiFraud(object):
# ) # )
if __name__ == "__main__":
antifraud = AntiFraud() antifraud = AntiFraud()

View File

@ -1 +1,2 @@
from core.lib.schemas import agora_s # noqa
from core.lib.schemas import nordigen_s # noqa from core.lib.schemas import nordigen_s # noqa

View File

@ -0,0 +1,86 @@
from pydantic import BaseModel
class ContactDataBuyerSeller(BaseModel):
username: str
name: str
feedback_score: int
trade_count: str
last_online: str
class ContactDataAd(BaseModel):
payment_method: str
trade_type: str
advertiser: ContactDataBuyerSeller
asset: str
id: str
class ContactData(BaseModel):
buyer: ContactDataBuyerSeller
seller: ContactDataBuyerSeller
amount: str
amount_xmr: str
fee_xmr: str
advertisement: ContactDataAd
contact_id: str
currency: str
country: str
account_info: str
price_equation: str
is_buying: bool
is_selling: bool
created_at: str
escrowed_at: str
funded_at: str
canceled_at: str | None
closed_at: str | None
msg: str
released_at: str | None
payment_completed_at: str | None
disputed_at: str | None
arbitrated: bool
transfer_to_seller_non_custodial_wallet_transaction_confirmations: str | None
transfer_to_buyer_settlement_wallet_transaction_id: str | None
transfer_to_buyer_settlement_wallet_transaction_key: str | None
buyer_settlement_address: str | None
buyer_settlement_fee_level: str | None
seller_non_custodial_wallet_mnemonic: str | None
transfer_to_seller_non_custodial_wallet_transaction_id: str | None
class ContactActions(BaseModel):
advertisement_public_view: str
advertisement_url: str
message_post_url: str
messages_url: str
release_url: str
class Contact(BaseModel):
data: ContactData
actions: ContactActions
class ResponseData(BaseModel):
contact_count: int
contact_list: list[Contact]
class Response(BaseModel):
data: ResponseData
class Dashboard(BaseModel):
success: bool
message: str
response: Response
DashboardSchema = {
"success": "success",
"message": "message",
"contact_count": "response.data.contact_count",
"contact_list": "response.data.contact_list",
}

View File

@ -122,6 +122,10 @@ class Platform(models.Model):
enabled = models.BooleanField(default=True) enabled = models.BooleanField(default=True)
@classmethod
def get_for_user(cls, user):
return cls.objects.filter(user=user, enabled=True)
@property @property
def currencies(self): def currencies(self):
return Aggregator.get_currencies_for_platform(self) return Aggregator.get_currencies_for_platform(self)

View File

@ -242,6 +242,12 @@
</a> </a>
<div class="navbar-dropdown"> <div class="navbar-dropdown">
<a class="navbar-item" href="{% url 'trades' type='page' %}">
Trades
</a>
<a class="navbar-item" href="#">
Wallets
</a>
<a class="navbar-item" href="#"> <a class="navbar-item" href="#">
Advert Configuration Advert Configuration
</a> </a>

View File

@ -0,0 +1,82 @@
{% load cache %}
{% load cachalot cache %}
{% get_last_invalidation 'core.Platform' as last %}
{% include 'mixins/partials/notify.html' %}
{# cache 600 objects_platform_trades request.user.id object_list type last #}
{% for platform_name, trade_map in object_list.items %}
<h2 class="title is-4">{{ platform_name }}</h2>
<table
class="table is-fullwidth is-hoverable"
hx-target="#{{ context_object_name }}-table"
id="{{ context_object_name }}-table"
hx-swap="outerHTML"
hx-trigger="{{ context_object_name_singular }}Event from:body"
hx-get="{{ list_url }}">
<thead>
<th>id</th>
<th>buyer</th>
<th>seller</th>
<th>amount</th>
<th>crypto</th>
<th>provider</th>
<th>type</th>
<th>actions</th>
</thead>
{% for item in trade_map.values %}
<tr>
<td>
<a
class="has-text-grey"
onclick="window.prompt('Copy to clipboard: Ctrl+C, Enter', '{{ item.data.contact_id }}/');">
<span class="icon" data-tooltip="Copy to clipboard">
<i class="fa-solid fa-copy" aria-hidden="true"></i>
</span>
</a>
</td>
<td data-tooltip="{{ item.data.buyer.name }}
Trades: {{ item.data.buyer.trade_count }}
Last online: {{ item.data.buyer.last_online }}">{{ item.data.buyer.username }}</td>
<td data-tooltip="{{ item.data.seller.name }}
Trades: {{ item.data.seller.trade_count }}
Last online: {{ item.data.seller.last_online }}">{{ item.data.seller.username }}</td>
<td>{{ item.data.amount }} {{ item.data.currency }}</td>
<td>{{ item.data.amount_xmr }} XMR</td>
<td>{{ item.data.advertisement.payment_method }}</td>
<td>{{ item.data.advertisement.trade_type }}</td>
<td>
<div class="buttons">
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-get="{# url 'platform_update' type=type pk=item.id #}"
hx-trigger="click"
hx-target="#{{ type }}s-here"
hx-swap="innerHTML"
class="button">
<span class="icon-text">
<span class="icon">
<i class="fa-solid fa-pencil"></i>
</span>
</span>
</button>
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-delete="{# url 'platform_delete' type=type pk=item.id #}"
hx-trigger="click"
hx-target="#modals-here"
hx-swap="innerHTML"
hx-confirm="Are you sure you wish to delete {{ item.name }}?"
class="button">
<span class="icon-text">
<span class="icon">
<i class="fa-solid fa-xmark"></i>
</span>
</span>
</button>
</div>
</td>
</tr>
{% endfor %}
</table>
{% endfor %}
{# endcache #}

View File

@ -11,16 +11,37 @@ from mixins.views import ( # ObjectRead,
) )
from two_factor.views.mixins import OTPRequiredMixin from two_factor.views.mixins import OTPRequiredMixin
# from core.clients.platforms.agora import AgoraClient from core.clients.platforms.agora import AgoraClient
from core.forms import PlatformForm from core.forms import PlatformForm
from core.models import Platform from core.models import Platform
from core.util import logs from core.util import logs
from core.views.helpers import synchronize_async_helper
# from core.views.helpers import synchronize_async_helper
log = logs.get_logger(__name__) log = logs.get_logger(__name__)
class PlatformTrades(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
list_template = "partials/platform-trades.html"
page_title = "List of trades"
list_url_name = "trades"
list_url_args = ["type"]
context_object_name_singular = "trade"
context_object_name = "trades"
def get_queryset(self, **kwargs):
platforms = Platform.get_for_user(self.request.user)
total_trades = {}
for platform in platforms:
run = synchronize_async_helper(AgoraClient(platform))
dash = synchronize_async_helper(run.wrap_dashboard())
print("DASH", dash)
total_trades[platform.name] = dash
return total_trades
class PlatformList(LoginRequiredMixin, OTPRequiredMixin, ObjectList): class PlatformList(LoginRequiredMixin, OTPRequiredMixin, ObjectList):
list_template = "partials/platform-list.html" list_template = "partials/platform-list.html"
model = Platform model = Platform

View File

@ -108,3 +108,6 @@ class AntiFraud(util.Base):
trade_id, trade_id,
f"Hi! To continue the trade, please complete the verification form: {auth_url}", f"Hi! To continue the trade, please complete the verification form: {auth_url}",
) )
antifraud = AntiFraud()