From 436d069ae7104639a6f0cde16332ec9a0e4fb4d4 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Sat, 11 Mar 2023 16:57:08 +0000 Subject: [PATCH] Fix cheat --- app/urls.py | 5 + core/clients/aggregators/nordigen.py | 1 - core/clients/platform.py | 255 ++++++++++++------------ core/clients/platforms/api/agoradesk.py | 19 +- core/lib/money.py | 1 - core/lib/schemas/agora_s.py | 228 ++++++++++++++++++++- core/lib/schemas/nordigen_s.py | 10 +- core/management/commands/polling.py | 3 +- core/views/ads.py | 27 ++- core/views/aggregators.py | 2 +- core/views/banks.py | 6 +- 11 files changed, 401 insertions(+), 156 deletions(-) diff --git a/app/urls.py b/app/urls.py index be090f3..1ae2369 100644 --- a/app/urls.py +++ b/app/urls.py @@ -185,6 +185,11 @@ urlpatterns = [ ads.AdRedist.as_view(), name="ad_redist", ), + path( + "ops/ads/cheat/", + ads.Cheat.as_view(), + name="cheat", + ), path( "profit//", profit.Profit.as_view(), diff --git a/core/clients/aggregators/nordigen.py b/core/clients/aggregators/nordigen.py index b494faa..bccf7d6 100644 --- a/core/clients/aggregators/nordigen.py +++ b/core/clients/aggregators/nordigen.py @@ -300,7 +300,6 @@ class NordigenClient(BaseClient, AggregatorClient): """ path = f"accounts/{account_id}/transactions" response = await self.call(path, schema="Transactions") - print("RESP", response["pending"]) parsed = response["booked"] self.normalise_transactions(parsed, state="booked") diff --git a/core/clients/platform.py b/core/clients/platform.py index d70f7cf..1c6334c 100644 --- a/core/clients/platform.py +++ b/core/clients/platform.py @@ -5,6 +5,7 @@ from datetime import datetime, timezone from random import choices from string import ascii_uppercase +from aiocoingecko import AsyncCoinGeckoAPISession from django.conf import settings from core.clients.platforms.api.agoradesk import AgoraDesk @@ -33,7 +34,37 @@ class LocalPlatformClient(ABC): Call a method using the self.api object. """ if hasattr(self.api, method): - return await getattr(self.api, method)(*args, **kwargs) + returned_429 = True + iterations = 0 + throttled = 0 + while returned_429 or iterations == 100: + response = await getattr(self.api, method)(*args, **kwargs) + if "status" in response: + if response["status"] == 429: # Too many requests + throttled += 1 + sleep_time = pow(throttled, 1.9) + log.info( + ( + f"Throttled {throttled} times while calling " + f"{method}, " + f"sleeping for {sleep_time} seconds" + ) + ) + # We're running in a thread, so this is fine + await asyncio.sleep(sleep_time) + elif response["status"] == 400: + raise Exception(response) + else: + if throttled != 0: + log.info( + f"Finally successful after {throttled}", + f" attempts to call {method}", + ) + returned_429 = False + throttled = 0 + iterations += 1 + + return response else: raise Exception(f"Method {method} not found in {self.name} API.") @@ -249,62 +280,53 @@ class LocalPlatformClient(ABC): async def enum_ad_ids(self, page=0): if self.name == "lbtc" and page == 0: page = 1 - ads = await self.api.ads(page=page) + ads = await self.call("ads", page=page) # ads = await self.api._api_call(api_method="ads", query_values={"page": page}) - if ads is False: - return False ads_total = [] if not ads["success"]: return False - for ad in ads["response"]["data"]["ad_list"]: + for ad in ads["ad_list"]: ads_total.append(ad["data"]["ad_id"]) - if "pagination" in ads["response"]: - if "next" in ads["response"]["pagination"]: - page += 1 - ads_iter = await self.enum_ad_ids(page) - if ads_iter is None: - return False - if ads_iter is False: - return False - for ad in ads_iter: - ads_total.append(ad) + if "pagination" in ads: + if ads["pagination"]: + if "next" in ads["pagination"]: + page += 1 + ads_iter = await self.enum_ad_ids(page) + if ads_iter is None: + return False + if ads_iter is False: + return False + for ad in ads_iter: + ads_total.append(ad) return ads_total async def enum_ads(self, requested_asset=None, page=0): - if self.name == "lbtc" and page == 0: - page = 1 query_values = {"page": page} if requested_asset: query_values["asset"] = requested_asset # ads = await self.api._api_call(api_method="ads", query_values=query_values) - ads = await self.api.ads(page=page) - if ads is False: - return False + ads = await self.call("ads", page=page) ads_total = [] if not ads["success"]: return False - if not ads["response"]: - return False - for ad in ads["response"]["data"]["ad_list"]: - if self.name == "agora": - asset = ad["data"]["asset"] - elif self.name == "lbtc": - asset = "BTC" + for ad in ads["ad_list"]: + asset = ad["data"]["asset"] ad_id = ad["data"]["ad_id"] country = ad["data"]["countrycode"] currency = ad["data"]["currency"] provider = ad["data"]["online_provider"] ads_total.append([asset, ad_id, country, currency, provider]) - if "pagination" in ads["response"]: - if "next" in ads["response"]["pagination"]: - page += 1 - ads_iter = await self.enum_ads(requested_asset, page) - if ads_iter is None: - return False - if ads_iter is False: - return False - for ad in ads_iter: - ads_total.append([ad[0], ad[1], ad[2], ad[3], ad[4]]) + if "pagination" in ads: + if ads["pagination"]: + if "next" in ads["pagination"]: + page += 1 + ads_iter = await self.enum_ads(requested_asset, page) + if ads_iter is None: + return False + if ads_iter is False: + return False + for ad in ads_iter: + ads_total.append([ad[0], ad[1], ad[2], ad[3], ad[4]]) return ads_total def last_online_recent(self, date): @@ -331,16 +353,8 @@ class LocalPlatformClient(ABC): sec_ago_date = (now - date_parsed).total_seconds() return sec_ago_date < 172800 - async def enum_public_ads(self, asset, currency, providers=None, page=0): - if self.name == "lbtc" and page == 0: - page = 1 + async def enum_public_ads(self, asset, currency, provider, page=0): to_return = [] - # if asset == "XMR": - # coin = "monero" - # elif asset == "BTC": - # coin = "bitcoins" - if not providers: - providers = ["NATIONAL_BANK"] # buy-monero-online, buy-bitcoin-online # Work around Agora weirdness calling it bitcoins # ads = await self.api._api_call( @@ -348,28 +362,31 @@ class LocalPlatformClient(ABC): # query_values={"page": page}, # ) if asset == "XMR": - ads = await self.api.buy_monero_online(currency_code=currency, page=page) + ads = await self.call( + "buy_monero_online", + currency_code=currency, + payment_method=provider, + page=page, + ) elif asset == "BTC": - ads = await self.api.buy_bitcoins_online(currency_code=currency, page=page) - # with open("pub.json", "a") as f: - # import json - # f.write(json.dumps([page, currency, asset, ads])+"\n") - # f.close() - if ads is None: + ads = await self.call( + "buy_bitcoins_online", + currency_code=currency, + payment_method=provider, + page=page, + ) + else: + raise Exception("Unknown asset") + if not ads["success"]: return False - if ads is False: - return False - if ads["response"] is None: - return False - if "data" not in ads["response"]: - return False - for ad in ads["response"]["data"]["ad_list"]: - provider = ad["data"]["online_provider"] + found_us = False + for ad in ads["ad_list"]: + provider_ad = ad["data"]["online_provider"] if self.name == "lbtc": - provider_test = self.map_provider(provider) + provider_test = self.map_provider(provider_ad) else: provider_test = provider - if provider_test not in providers: + if provider_test != provider: continue date_last_seen = ad["data"]["profile"]["last_online"] # Check if this person was seen recently @@ -377,48 +394,36 @@ class LocalPlatformClient(ABC): continue ad_id = str(ad["data"]["ad_id"]) username = ad["data"]["profile"]["username"] + if username == self.instance.username: + found_us = True temp_price = ad["data"]["temp_price"] if ad["data"]["currency"] != currency: continue - to_append = [ad_id, username, temp_price, provider, asset, currency] + to_append = [ad_id, username, temp_price, provider_ad, asset, currency] if to_append not in to_return: to_return.append(to_append) # await [ad_id, username, temp_price, provider, asset, currency] - if "pagination" in ads["response"]: - if "next" in ads["response"]["pagination"]: - page += 1 - ads_iter = await self.enum_public_ads(asset, currency, providers, page) - if ads_iter is None: - return False - if ads_iter is False: - return False - for ad in ads_iter: - to_append = [ad[0], ad[1], ad[2], ad[3], ad[4], ad[5]] - if to_append not in to_return: - to_return.append(to_append) + + if found_us: + return to_return + if "pagination" in ads: + if ads["pagination"]: + if "next" in ads["pagination"]: + page += 1 + ads_iter = await self.enum_public_ads( + asset, currency, provider, page + ) + if ads_iter is None: + return False + if ads_iter is False: + return False + for ad in ads_iter: + to_append = [ad[0], ad[1], ad[2], ad[3], ad[4], ad[5]] + if to_append not in to_return: + to_return.append(to_append) return to_return - async def run_cheat_in_thread(self, assets=None): - """ - Update prices in another thread. - """ - if not assets: - all_assets = ["XMR"] - assets_not_run = set(all_assets) ^ set(self.cheat_run_on) - if not assets_not_run: - self.cheat_run_on = [] - asset = list(all_assets).pop() - self.cheat_run_on.append(asset) - else: - asset = assets_not_run.pop() - self.cheat_run_on.append(asset) - await self.update_prices([asset]) - return asset - else: - # deferToThread(self.update_prices, assets) - await self.update_prices(assets) - - async def update_prices(self, assets=None): + async def cheat(self, assets=None): # Get all public ads for the given assets public_ads = await self.get_all_public_ads(assets) if not public_ads: @@ -446,33 +451,35 @@ class LocalPlatformClient(ABC): providers = providers or self.instance.ads_providers currencies = currencies or self.instance.currencies # We want to get the ads for each of these currencies and return the result - rates = await money.cg.get_price( - ids=["monero", "bitcoin"], vs_currencies=currencies - ) + async with AsyncCoinGeckoAPISession() as cg: + rates = await cg.get_price( + ids="monero,bitcoin", vs_currencies=",".join(currencies) + ) for asset in assets: for currency in currencies: - cg_asset_name = crypto_map[asset] - try: - rates[cg_asset_name][currency.lower()] - except KeyError: - log.debug(f"Error getting public ads for currency: {currency}") - continue - ads_list = await self.enum_public_ads(asset, currency, providers) - if not ads_list: - log.debug("Error getting ads list.") - continue - ads = await money.lookup_rates(self.name, ads_list, rates=rates) - if not ads: - log.debug("Error lookup up rates.") - continue - log.debug("Writing to ES.") - await self.write_to_es_ads("ads", ads) - if currency in public_ads: - for ad in list(ads): - if ad not in public_ads[currency]: - public_ads[currency].append(ad) - else: - public_ads[currency] = ads + for provider in providers: + cg_asset_name = crypto_map[asset] + try: + rates[cg_asset_name][currency.lower()] + except KeyError: + log.debug(f"Error getting public ads for currency: {currency}") + continue + ads_list = await self.enum_public_ads(asset, currency, provider) + if not ads_list: + log.debug("Error getting ads list.") + continue + ads = await money.lookup_rates(self.name, ads_list, rates=rates) + if not ads: + log.debug("Error lookup up rates.") + continue + log.debug("Writing to ES.") + # await self.write_to_es_ads("ads", ads) + if currency in public_ads: + for ad in list(ads): + if ad not in public_ads[currency]: + public_ads[currency].append(ad) + else: + public_ads[currency] = ads return public_ads @@ -980,7 +987,7 @@ class LocalPlatformClient(ABC): # (asset, currency, provider) assets = self.instance.ads_assets - currencies = self.currencies + currencies = self.instance.currencies providers = self.instance.ads_providers # if platform == "lbtc": # providers = [ @@ -1161,7 +1168,7 @@ class LocalPlatformClient(ABC): currency_account_info_map[currency]["recipient"] = account[ "ownerName" ] - return (currencies, currency_account_info_map) + return (list(currency_account_info_map.keys()), currency_account_info_map) def get_matching_account_details(self, currency, ad): ( diff --git a/core/clients/platforms/api/agoradesk.py b/core/clients/platforms/api/agoradesk.py index 3fe913e..4f45be9 100644 --- a/core/clients/platforms/api/agoradesk.py +++ b/core/clients/platforms/api/agoradesk.py @@ -652,7 +652,7 @@ class AgoraDesk: ) @staticmethod - async def _generic_search_parameters(amount, page): + def _generic_search_parameters(amount, page): params = None if amount and page is not None: params = {"amount": f"{amount}"} @@ -662,6 +662,7 @@ class AgoraDesk: params = {"page": f"{page}"} return params + # async def buy_monero_online( self, currency_code: str, @@ -680,7 +681,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_online( + return await self._generic_online( direction="buy", main_currency="monero", exchange_currency=currency_code, @@ -708,7 +709,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_online( + return await self._generic_online( direction="buy", main_currency="bitcoins", exchange_currency=currency_code, @@ -736,7 +737,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_online( + return await self._generic_online( direction="sell", main_currency="monero", exchange_currency=currency_code, @@ -764,7 +765,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_online( + return await self._generic_online( direction="sell", main_currency="bitcoins", exchange_currency=currency_code, @@ -811,7 +812,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_cash( + return await self._generic_cash( direction="buy", main_currency="monero", exchange_currency=currency_code, @@ -838,7 +839,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_cash( + return await self._generic_cash( direction="buy", main_currency="bitcoins", exchange_currency=currency_code, @@ -865,7 +866,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_cash( + return await self._generic_cash( direction="sell", main_currency="monero", exchange_currency=currency_code, @@ -892,7 +893,7 @@ class AgoraDesk: # pylint: disable=too-many-arguments - return self._generic_cash( + return await self._generic_cash( direction="sell", main_currency="bitcoins", exchange_currency=currency_code, diff --git a/core/lib/money.py b/core/lib/money.py index b4d7e38..4fb1042 100644 --- a/core/lib/money.py +++ b/core/lib/money.py @@ -390,7 +390,6 @@ class Money(object): cast_es["total_remaining"] = total_remaining cast_es["total_profit"] = total_profit # await self.write_to_es("get_total", cast_es) - print("CAST ES", cast_es) return cast_es async def open_trades_usd_parse_dash(self, dash, rates): diff --git a/core/lib/schemas/agora_s.py b/core/lib/schemas/agora_s.py index cd98af3..c8c1f6a 100644 --- a/core/lib/schemas/agora_s.py +++ b/core/lib/schemas/agora_s.py @@ -1,7 +1,12 @@ -from pydantic import BaseModel +from pydantic import BaseModel, Extra -class ContactDataBuyerSeller(BaseModel): +class MyModel(BaseModel): + class Config: + extra = Extra.forbid + + +class ContactDataBuyerSeller(MyModel): username: str name: str feedback_score: int @@ -9,7 +14,7 @@ class ContactDataBuyerSeller(BaseModel): last_online: str -class ContactDataAd(BaseModel): +class ContactDataAd(MyModel): payment_method: str trade_type: str advertiser: ContactDataBuyerSeller @@ -18,7 +23,7 @@ class ContactDataAd(BaseModel): contact_id: str | None -class ContactData(BaseModel): +class ContactData(MyModel): buyer: ContactDataBuyerSeller seller: ContactDataBuyerSeller amount: str @@ -53,32 +58,34 @@ class ContactData(BaseModel): transfer_to_seller_non_custodial_wallet_transaction_id: str | None -class ContactActions(BaseModel): +class ContactActions(MyModel): advertisement_public_view: str | None advertisement_url: str | None message_post_url: str messages_url: str release_url: str + cancel_url: str | None -class Contact(BaseModel): +class Contact(MyModel): data: ContactData actions: ContactActions -class ResponseData(BaseModel): +class DashboardResponseData(MyModel): contact_count: int contact_list: list[Contact] -class Response(BaseModel): - data: ResponseData +class DashboardResponse(MyModel): + data: DashboardResponseData -class Dashboard(BaseModel): +class Dashboard(MyModel): success: bool + status: int | None message: str - response: Response + response: DashboardResponse DashboardSchema = { @@ -87,3 +94,202 @@ DashboardSchema = { "contact_count": "response.data.contact_count", "contact_list": "response.data.contact_list", } + + +class Pagination(MyModel): + prev: str | None + next: str | None + total_elements: int + total_pages: int + current_page: int + + +class Profile(MyModel): + username: str + name: str + feedback_score: int + trade_count: str + last_online: str + localbitcoins_trade_count: int | None + paxful_trade_count: int | None + + +class BuyBitcoinsOnlineAd(MyModel): + ad_id: str + countrycode: str + created_at: str + currency: str + max_amount: float | None + max_amount_available: float + min_amount: float | None + msg: str + online_provider: str + require_trusted_by_advertiser: bool + verified_email_required: bool + temp_price: float + track_max_amount: bool + trade_type: str + trusted_required: bool + visible: bool + asset: str + payment_method_detail: str | None + profile: Profile + require_feedback_score: int | None + first_time_limit_btc: float | None + limit_to_fiat_amounts: str | None + + +class Actions(MyModel): + public_view: str + + +class BuyBitcoinsOnlineResponseDataAdList(MyModel): + data: BuyBitcoinsOnlineAd + actions: Actions + + +class BuyBitcoinsOnlineResponseData(MyModel): + ad_count: int + ad_list: list[BuyBitcoinsOnlineResponseDataAdList] + + +class BuyBitcoinsOnlineResponse(MyModel): + data: BuyBitcoinsOnlineResponseData + pagination: Pagination | None + + +class BuyBitcoinsOnline(MyModel): + success: bool + message: str + response: BuyBitcoinsOnlineResponse + status: int | None + + +class BuyMoneroOnlineAd(MyModel): + ad_id: str + countrycode: str + created_at: str + currency: str + max_amount: float | None + max_amount_available: float + min_amount: float | None + msg: str + online_provider: str + require_trusted_by_advertiser: bool + verified_email_required: bool + temp_price: float + track_max_amount: bool + trade_type: str + trusted_required: bool + visible: bool + asset: str + payment_method_detail: str | None + profile: Profile + require_feedback_score: int | None + first_time_limit_xmr: float | None + limit_to_fiat_amounts: str | None + + +class BuyMoneroOnlineAdList(MyModel): + data: BuyMoneroOnlineAd + actions: Actions + + +class BuyMoneroOnlineResponseData(MyModel): + ad_count: int + ad_list: list[BuyMoneroOnlineAdList] + + +class BuyMoneroOnlineResponse(MyModel): + data: BuyMoneroOnlineResponseData + pagination: Pagination | None + + +class BuyMoneroOnline(MyModel): + success: bool + message: str + response: BuyMoneroOnlineResponse + status: int | None + + +BuyBitcoinsOnlineSchema = { + "success": "success", + "message": "message", + "ad_count": "response.data.ad_count", + "ad_list": "response.data.ad_list", + "pagination": "response.pagination", +} + +BuyMoneroOnlineSchema = { + "success": "success", + "message": "message", + "ad_count": "response.data.ad_count", + "ad_list": "response.data.ad_list", + "pagination": "response.pagination", +} + + +class AccountInfo(MyModel): + ... + + +class AdsResponseDataAd(MyModel): + ad_id: str + countrycode: str + created_at: str + currency: str + max_amount: float | None + max_amount_available: float + min_amount: float | None + msg: str + online_provider: str + require_trusted_by_advertiser: bool + verified_email_required: bool + temp_price: float + track_max_amount: bool + trade_type: str + trusted_required: bool + visible: bool + asset: str + payment_method_detail: str | None + require_feedback_score: int | None + first_time_limit_xmr: float | None + first_time_limit_btc: float | None + limit_to_fiat_amounts: str | None + account_info: str + price_equation: str + + +class AdsActions(MyModel): + change_form: str | None + html_form: str | None + public_view: str | None + + +class AdsResponseDataAdList(MyModel): + data: AdsResponseDataAd + actions: AdsActions + + +class AdsResponseData(MyModel): + ad_count: int + ad_list: list[AdsResponseDataAdList] + + +class AdsResponse(MyModel): + data: AdsResponseData + + +class Ads(MyModel): + success: bool + message: str + response: AdsResponse + status: int | None + + +AdsSchema = { + "success": "success", + "message": "message", + "ad_count": "response.data.ad_count", + "ad_list": "response.data.ad_list", +} diff --git a/core/lib/schemas/nordigen_s.py b/core/lib/schemas/nordigen_s.py index 6aaaa69..ee26cc5 100644 --- a/core/lib/schemas/nordigen_s.py +++ b/core/lib/schemas/nordigen_s.py @@ -1,4 +1,12 @@ -from pydantic import BaseModel +from pydantic import BaseModel, Extra + + +class MyModel(BaseModel): + class Config: + extra = Extra.forbid + + +# TODO: inherit from MyModel class TokenNew(BaseModel): diff --git a/core/management/commands/polling.py b/core/management/commands/polling.py index 0785c57..d9b5e41 100644 --- a/core/management/commands/polling.py +++ b/core/management/commands/polling.py @@ -14,11 +14,10 @@ INTERVAL = 5 async def poll_aggregator(aggregator): - print("Polling aggregator", aggregator) + pass async def poll_platform(platform): - print("Polling platform", platform) client = await AgoraClient(platform) await client.poll() diff --git a/core/views/ads.py b/core/views/ads.py index 8770280..998d766 100644 --- a/core/views/ads.py +++ b/core/views/ads.py @@ -11,6 +11,20 @@ from core.models import Ad from core.views.helpers import synchronize_async_helper +class Cheat(LoginRequiredMixin, OTPRequiredMixin, View): + template_name = "mixins/partials/notify.html" + + def get(self, request): + ads = Ad.objects.filter(user=request.user, enabled=True) + for ad in ads: + for platform in ad.platforms.all(): + run = synchronize_async_helper(AgoraClient(platform)) + synchronize_async_helper(run.cheat()) + + context = {"class": "success", "message": "Cheat run"} + return render(request, self.template_name, context) + + class AdNuke(LoginRequiredMixin, OTPRequiredMixin, View): template_name = "mixins/partials/notify.html" @@ -21,7 +35,7 @@ class AdNuke(LoginRequiredMixin, OTPRequiredMixin, View): run = synchronize_async_helper(AgoraClient(platform)) synchronize_async_helper(run.nuke_ads()) - context = {"class": "success", "message": "Nuking ads"} + context = {"class": "success", "message": "Ads nuked"} return render(request, self.template_name, context) @@ -35,7 +49,7 @@ class AdDist(LoginRequiredMixin, OTPRequiredMixin, View): run = synchronize_async_helper(AgoraClient(platform)) synchronize_async_helper(run.dist_countries(ad)) - context = {"class": "success", "message": "Distributing ads"} + context = {"class": "success", "message": "Ads distributed"} return render(request, self.template_name, context) @@ -49,7 +63,7 @@ class AdRedist(LoginRequiredMixin, OTPRequiredMixin, View): run = synchronize_async_helper(AgoraClient(platform)) synchronize_async_helper(run.redist_countries(ad)) - context = {"class": "success", "message": "Updating ads"} + context = {"class": "success", "message": "Ads updated"} return render(request, self.template_name, context) @@ -80,6 +94,13 @@ class AdList(LoginRequiredMixin, OTPRequiredMixin, ObjectList): "label": "Update ads", "icon": "fa-solid fa-refresh", }, + { + "url": reverse("cheat"), + "action": "cheat", + "method": "get", + "label": "Run cheat", + "icon": "fa-solid fa-bolt", + }, { "url": reverse("ad_nuke"), "action": "nuke", diff --git a/core/views/aggregators.py b/core/views/aggregators.py index 66bfc21..ccd0881 100644 --- a/core/views/aggregators.py +++ b/core/views/aggregators.py @@ -50,7 +50,7 @@ class RequestBankFetch(LoginRequiredMixin, OTPRequiredMixin, View): class ReqsList(LoginRequiredMixin, OTPRequiredMixin, ObjectList): list_template = "partials/aggregator-info.html" - page_title = "Aggregator Info" + page_title = "Aggregator info" context_object_name_singular = "requisition" context_object_name = "requisitions" diff --git a/core/views/banks.py b/core/views/banks.py index 658a359..afeca17 100644 --- a/core/views/banks.py +++ b/core/views/banks.py @@ -17,7 +17,7 @@ class BanksCurrencies(LoginRequiredMixin, OTPRequiredMixin, ObjectList): """ list_template = "partials/banks-currencies-list.html" - page_title = "Bank Currencies" + page_title = "Bank currencies" context_object_name_singular = "currency" context_object_name = "currencies" @@ -58,7 +58,7 @@ class BanksBalances(LoginRequiredMixin, OTPRequiredMixin, ObjectList): """ list_template = "partials/banks-balances-list.html" - page_title = "Bank Balances" + page_title = "Bank balances" context_object_name_singular = "balance" context_object_name = "balances" @@ -100,7 +100,7 @@ class BanksTransactions(LoginRequiredMixin, OTPRequiredMixin, ObjectList): """ list_template = "partials/banks-transactions-list.html" - page_title = "Bank Transactions" + page_title = "Bank transactions" context_object_name_singular = "transaction" context_object_name = "transactions"