From 896318982ff7a737c1ab2d586f61222c81b8b576 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Tue, 22 Feb 2022 19:53:22 +0000 Subject: [PATCH] Move some functions to a util class from Agora --- handler/agora.py | 75 +++++++++---------------------------- handler/tests/test_agora.py | 8 +++- handler/util.py | 48 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 60 deletions(-) diff --git a/handler/agora.py b/handler/agora.py index b8e1fe3..ed08e87 100644 --- a/handler/agora.py +++ b/handler/agora.py @@ -7,42 +7,16 @@ from twisted.internet.threads import deferToThread from json import loads from forex_python.converter import CurrencyRates from agoradesk_py import AgoraDesk -from httpx import ReadTimeout, ReadError, RemoteProtocolError -from pycoingecko import CoinGeckoAPI -from datetime import datetime +from pycoingecko import CoinGeckoAPI # TODO: remove this import and defer to money from time import sleep from pyotp import TOTP # Project imports from settings import settings +import util log = Logger("agora.global") -# TODO: move to utils - - -def handle_exceptions(func): - def inner_function(*args, **kwargs): - try: - rtrn = func(*args, **kwargs) - except (ReadTimeout, ReadError, RemoteProtocolError): - return False - if isinstance(rtrn, dict): - if "success" in rtrn: - if "message" in rtrn: - if not rtrn["success"] and rtrn["message"] == "API ERROR": - if "error_code" in rtrn["response"]["error"]: - code = rtrn["response"]["error"]["error_code"] - if not code == 136: - log.error("API error: {code}", code=code) - return False - else: - log.error("API error: {code}", code=rtrn["response"]["error"]) - return False - return rtrn - - return inner_function - class Agora(object): """ @@ -56,8 +30,8 @@ class Agora(object): """ self.log = Logger("agora") self.agora = AgoraDesk(settings.Agora.Token) - self.cr = CurrencyRates() - self.cg = CoinGeckoAPI() + self.cr = CurrencyRates() # TODO: remove this and defer to money + self.cg = CoinGeckoAPI() # TODO: remove this and defer to money # Cache for detecting new trades self.last_dash = set() @@ -78,7 +52,7 @@ class Agora(object): self.lc_cheat = LoopingCall(self.run_cheat_in_thread) self.lc_cheat.start(int(settings.Agora.CheatSec)) - @handle_exceptions + @util.handle_exceptions def wrap_dashboard(self): dash = self.agora.dashboard_seller() if dash is None: @@ -179,7 +153,7 @@ class Agora(object): current_trades.append(reference) self.tx.cleanup(current_trades) - @handle_exceptions + @util.handle_exceptions def get_recent_messages(self, send_irc=True): """ Get recent messages. @@ -226,7 +200,7 @@ class Agora(object): return messages_tmp - @handle_exceptions + @util.handle_exceptions def enum_ad_ids(self, page=0): ads = self.agora._api_call(api_method="ads", query_values={"page": page}) if ads is False: @@ -248,7 +222,7 @@ class Agora(object): ads_total.append(ad) return ads_total - @handle_exceptions + @util.handle_exceptions def enum_ads(self, requested_asset=None, page=0): query_values = {"page": page} if requested_asset: @@ -278,22 +252,7 @@ class Agora(object): ads_total.append([ad[0], ad[1], ad[2], ad[3], ad[4]]) return ads_total - # TODO: move to utils library - def last_online_recent(self, date): - """ - Check if the last online date was recent. - :param date: date last online - :type date: string - :return: bool indicating whether the date was recent enough - :rtype: bool - """ - date_parsed = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ") - now = datetime.now() - sec_ago_date = (now - date_parsed).total_seconds() - # self.log.debug("Seconds ago date for {date} ^ {now}: {x}", date=date, now=str(now), x=sec_ago_date) - return sec_ago_date < 172800 - - @handle_exceptions + @util.handle_exceptions def enum_public_ads(self, asset, currency, providers=None, page=0): to_return = [] @@ -326,7 +285,7 @@ class Agora(object): continue date_last_seen = ad["data"]["profile"]["last_online"] # Check if this person was seen recently - if not self.last_online_recent(date_last_seen): + if not util.last_online_recent(date_last_seen): continue ad_id = ad["data"]["ad_id"] username = ad["data"]["profile"]["username"] @@ -391,7 +350,7 @@ class Agora(object): else: deferToThread(self.update_prices, assets) - @handle_exceptions + @util.handle_exceptions def update_prices(self, assets=None): # Get all public ads for the given assets public_ads = self.get_all_public_ads(assets) @@ -403,7 +362,7 @@ class Agora(object): self.slow_ad_update(to_update) # TODO: make generic and move to markets - @handle_exceptions + @util.handle_exceptions def get_all_public_ads(self, assets=None, currencies=None, providers=None): """ Get all public ads for our listed currencies. @@ -487,7 +446,7 @@ class Agora(object): continue iterations += 1 - @handle_exceptions + @util.handle_exceptions def nuke_ads(self): """ Delete all of our adverts. @@ -534,7 +493,7 @@ class Agora(object): max_local = max_usd * rates[currency] return (min_local, max_local) - @handle_exceptions + @util.handle_exceptions def create_ad(self, asset, countrycode, currency, provider, edit=False, ad_id=None): """ Post an ad with the given asset in a country with a given currency. @@ -654,7 +613,7 @@ class Agora(object): return False yield (rtrn, ad_id) - @handle_exceptions + @util.handle_exceptions def strip_duplicate_ads(self): """ Remove duplicate ads. @@ -676,7 +635,7 @@ class Agora(object): actioned.append(rtrn["success"]) return all(actioned) - @handle_exceptions + @util.handle_exceptions def release_funds(self, contact_id): """ Release funds for a contact_id. @@ -695,7 +654,7 @@ class Agora(object): return rtrn # TODO: write test before re-enabling adding total_trades - @handle_exceptions + @util.handle_exceptions def withdraw_funds(self): """ Withdraw excess funds to our XMR wallets. diff --git a/handler/tests/test_agora.py b/handler/tests/test_agora.py index 73d15cd..b52c445 100644 --- a/handler/tests/test_agora.py +++ b/handler/tests/test_agora.py @@ -6,6 +6,7 @@ from copy import deepcopy from tests.common import fake_public_ads, cg_prices, expected_to_update from agora import Agora from markets import Markets +import util class TestAgora(TestCase): @@ -120,6 +121,9 @@ class TestAgora(TestCase): self.agora.last_online_recent = MagicMock() self.agora.last_online_recent.return_value = True + util.last_online_recent = MagicMock() + util.last_online_recent.return_value = True + # Override get_price self.agora.cg.get_price = MagicMock() self.agora.cg.get_price.return_value = cg_prices @@ -132,8 +136,8 @@ class TestAgora(TestCase): def test_enum_public_ads(self): # Override enum_public_ads self.agora.agora._api_call = self.mock_enum_public_ads_api_call - self.agora.last_online_recent = MagicMock() - self.agora.last_online_recent.return_value = True + util.last_online_recent = MagicMock() + util.last_online_recent.return_value = True enum_ads_return = self.agora.enum_public_ads("XMR", "USD", self.all_providers) diff --git a/handler/util.py b/handler/util.py index 9133ae8..ed19ef1 100644 --- a/handler/util.py +++ b/handler/util.py @@ -1,3 +1,13 @@ +# Twisted/Klein imports +from twisted.logger import Logger + +# Other library imports +from httpx import ReadTimeout, ReadError, RemoteProtocolError +from datetime import datetime + +log = Logger("util.global") + + def convert(data): """ Recursively convert a dictionary. @@ -11,3 +21,41 @@ def convert(data): if isinstance(data, list): return list(map(convert, data)) return data + + +def last_online_recent(date): + """ + Check if the last online date was recent. + :param date: date last online + :type date: string + :return: bool indicating whether the date was recent enough + :rtype: bool + """ + date_parsed = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ") + now = datetime.now() + sec_ago_date = (now - date_parsed).total_seconds() + # self.log.debug("Seconds ago date for {date} ^ {now}: {x}", date=date, now=str(now), x=sec_ago_date) + return sec_ago_date < 172800 + + +def handle_exceptions(func): + def inner_function(*args, **kwargs): + try: + rtrn = func(*args, **kwargs) + except (ReadTimeout, ReadError, RemoteProtocolError): + return False + if isinstance(rtrn, dict): + if "success" in rtrn: + if "message" in rtrn: + if not rtrn["success"] and rtrn["message"] == "API ERROR": + if "error_code" in rtrn["response"]["error"]: + code = rtrn["response"]["error"]["error_code"] + if not code == 136: + log.error("API error: {code}", code=code) + return False + else: + log.error("API error: {code}", code=rtrn["response"]["error"]) + return False + return rtrn + + return inner_function