def test_no_separators(self, m): self.assertEqual( RegexParser("usdrub", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) self.assertEqual( RegexParser("scburst", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="SC", to_currency="BURST", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) self.assertEqual( RegexParser("burstsc", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="BURST", to_currency="SC", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) self.assertEqual( RegexParser("scsc", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="SC", to_currency="SC", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) self.assertEqual( RegexParser("burstburst", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="BURST", to_currency="BURST", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), )
def test_ok_digits_l2r(self, m): # pair, space separator self.assertEqual( RegexParser("100.20 usd rub", 1, "en", "USD", True).parse(), PriceRequest( amount=Decimal("100.20"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) # single from, space separator self.assertEqual( RegexParser("100.20 rub", 1, "en", "USD", True).parse(), PriceRequest( amount=Decimal("100.20"), currency="RUB", to_currency="USD", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) # single from, no space separator self.assertEqual( RegexParser("100.20rub", 1, "en", "USD", True).parse(), PriceRequest( amount=Decimal("100.20"), currency="RUB", to_currency="USD", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) # single to, no space separator self.assertEqual( RegexParser("100.20rub", 1, "en", "USD", False).parse(), PriceRequest( amount=Decimal("100.20"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), )
def parse(self) -> PriceRequest: text = self.text obj = AMOUNT_PATTERN_COMPILED.match(text) if not obj: raise WrongFormatException amount = obj[0] if amount: amount = parse_amount(amount, self.locale) last_request = (Session.query(ChatRequests).filter_by( chat_id=self.chat_id).order_by( ChatRequests.modified_at.desc()).first()) if not last_request: raise WrongFormatException locale = Locale(self.locale) if locale.character_order == "right-to-left": direction_writing = DirectionWriting.RIGHT2LEFT else: direction_writing = DirectionWriting.LEFT2RIGHT return PriceRequest( amount=amount, currency=last_request.from_currency.code, to_currency=last_request.to_currency.code, parser_name=self.name, direction_writing=direction_writing, )
def test_ok_pair_no_digits(self, m): # pair low case, no digits, space separator self.assertEqual( RegexParser("usd rub", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) # pair upper case, no digits, space separator self.assertEqual( RegexParser("USD EUR", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="USD", to_currency="EUR", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) # pair min and max len, no digits, space separator self.assertEqual( RegexParser("sc burst", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="SC", to_currency="BURST", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) # pair min and max len, no digits, space separator self.assertEqual( RegexParser("burst sc", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="BURST", to_currency="SC", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), )
def test_ok_separators(self, m): self.assertEqual( RegexParser("usd to rub", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) self.assertEqual( RegexParser("usd=rub", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) self.assertEqual( RegexParser("100.20 usd in rub", 1, "en", "USD", True).parse(), PriceRequest( amount=Decimal("100.20"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) self.assertEqual( RegexParser("rub = usd 100.20", 1, "en", "USD", True).parse(), PriceRequest( amount=Decimal("100.20"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.RIGHT2LEFT, ), )
def test_ok_single_no_digits(self, m): # single to, no digits self.assertEqual( RegexParser("rub", 1, "en", "USD", False).parse(), PriceRequest( amount=None, currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), ) # single from, no digits self.assertEqual( RegexParser("rub", 1, "en", "USD", True).parse(), PriceRequest( amount=None, currency="RUB", to_currency="USD", parser_name="RegexParser", direction_writing=DirectionWriting.UNKNOWN, ), )
def test_locale(self): prr = PriceRequestResult( price_request=PriceRequest( amount=None, currency="USD", to_currency="EUR", parser_name="test-parser", direction_writing=DirectionWriting.UNKNOWN, ), exchanges=["test-exchanger"], rate=Decimal("1.5"), rate_open=Decimal("1.3"), low24h=Decimal("1.2"), high24h=Decimal("1.6"), last_trade_at=datetime.now(), ) fpr = FormatPriceRequestResult(prr, "en") self.assertTrue(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual( fpr.get(), "*USD EUR* 1.5 ⬆️\n+0.2 (+15.38%)\n*Low*: 1.2 *High*: 1.6\n_17 March, 22:14 UTC_\ntest-exchanger 📡", ) fpr = FormatPriceRequestResult(prr, "de") self.assertTrue(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual( fpr.get(), "*USD EUR* 1,5 ⬆️\n+0,2 (+15,38%)\n*Low*: 1,2 *High*: 1,6\n_17 March, 22:14 UTC_\ntest-exchanger 📡", ) fpr = FormatPriceRequestResult(prr, "zh-hans-sg") self.assertTrue(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual( fpr.get(), "*USD EUR* 1.5 ⬆️\n+0.2 (+15.38%)\n*Low*: 1.2 *High*: 1.6\n_17 March, 22:14 UTC_\ntest-exchanger 📡", )
def test_price_mode_no_diff(self): prr = PriceRequestResult( price_request=PriceRequest( amount=None, currency="USD", to_currency="EUR", parser_name="test-parser", direction_writing=DirectionWriting.UNKNOWN, ), exchanges=["test-exchanger"], rate=Decimal("1.5"), last_trade_at=datetime.now(), ) fpr = NotifyFormatPriceRequestResult(prr, "en") self.assertFalse(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual( fpr.get(), "*USD EUR* 1.5 🔔\n_17 March, 22:14 UTC_\ntest-exchanger 📡")
def test_price_mode_no_diff(self): prr = PriceRequestResult( price_request=PriceRequest( amount=None, currency="USD", to_currency="EUR", parser_name="test-parser", direction_writing=DirectionWriting.UNKNOWN, ), exchanges=["test-exchanger"], rate=Decimal("1.5"), rate_open=None, low24h=Decimal("1.2"), high24h=Decimal("1.6"), last_trade_at=datetime.now(), ) fpr = InlineFormatPriceRequestResult(prr, "en") self.assertFalse(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual(fpr.get(), "USD EUR 1.5")
def test_convert_r2l(self): prr = PriceRequestResult( price_request=PriceRequest( amount=Decimal("0.5"), currency="USD", to_currency="EUR", parser_name="test-parser", direction_writing=DirectionWriting.RIGHT2LEFT, ), exchanges=["test-exchanger"], rate=Decimal("3"), rate_open=Decimal("1"), low24h=Decimal("1"), high24h=Decimal("1"), last_trade_at=datetime.now(), ) fpr = InlineFormatPriceRequestResult(prr, "en") self.assertTrue(fpr.is_rate_diff_available()) self.assertTrue(fpr.is_convert_mode()) self.assertEqual(fpr.get(), "1.5 EUR = 0.5 USD")
def test_convert_mode_1996(self): prr = PriceRequestResult( price_request=PriceRequest( amount=Decimal("0.5"), currency="USD", to_currency="USD", parser_name="test-parser", direction_writing=DirectionWriting.UNKNOWN, ), exchanges=["test-exchanger"], rate=Decimal("3"), rate_open=Decimal("1"), low24h=Decimal("1"), high24h=Decimal("1"), last_trade_at=datetime.now(), ) fpr = FormatPriceRequestResult(prr, "en") self.assertTrue(fpr.is_rate_diff_available()) self.assertTrue(fpr.is_convert_mode()) self.assertEqual( fpr.get(), "0.5 *USD* = 1.5 *USD*\n_17 March 1996_\ntest-exchanger 📡")
def test_money_formats(self, m): self.assertEqual( RegexParser("100.20 usd rub", 1, "en", "USD", True).parse(), PriceRequest( amount=Decimal("100.20"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) self.assertEqual( RegexParser("100.20 usd rub", 1, "de", "USD", True).parse(), PriceRequest( amount=Decimal("10020"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) self.assertEqual( RegexParser("100,20 usd rub", 1, "en", "USD", True).parse(), PriceRequest( amount=Decimal("10020"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) self.assertEqual( RegexParser("100,20 usd rub", 1, "de", "USD", True).parse(), PriceRequest( amount=Decimal("100.20"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) self.assertEqual( RegexParser("100 20 usd rub", 1, "de", "USD", True).parse(), PriceRequest( amount=Decimal("10020"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), ) self.assertEqual( RegexParser("100 20,12 usd rub", 1, "de", "USD", True).parse(), PriceRequest( amount=Decimal("10020.12"), currency="USD", to_currency="RUB", parser_name="RegexParser", direction_writing=DirectionWriting.LEFT2RIGHT, ), )
def inline_query_callback(update: Update, context: CallbackContext, chat_info: dict): query = update.inline_query.query if not query: logging.info("inline_request empty query") last_requests = get_last_request(update.effective_user.id) results = [] for r in last_requests: if not r.from_currency.is_active or not r.to_currency.is_active: continue try: price_request_result = convert( PriceRequest( amount=None, currency=r.from_currency.code, to_currency=r.to_currency.code, parser_name="InlineQuery", )) except NoRatesException: continue title = InlineFormatPriceRequestResult(price_request_result, chat_info["locale"]).get() text_to = FormatPriceRequestResult(price_request_result, chat_info["locale"]).get() ident = ( f"{r.from_currency.code}{r.to_currency.code}" f"{price_request_result.rate}{price_request_result.last_trade_at}" ) results.append( InlineQueryResultArticle( id=ident, title=title, input_message_content=InputTextMessageContent( text_to, disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN, ), )) write_request_log.delay( chat_id=update.effective_user.id, msg="", created_at=datetime.now(), tag="Inline All", ) else: tag = "" try: price_request = start_parse( query, chat_info["chat_id"], chat_info["locale"], chat_info["default_currency"], chat_info["default_currency_position"], ) logging.info(f"inline_request: {query} -> {price_request}") tag = price_request.parser_name price_request_result = convert(price_request) logging.info(f"inline_request: {price_request_result}") title = InlineFormatPriceRequestResult(price_request_result, chat_info["locale"]).get() text_to = FormatPriceRequestResult(price_request_result, chat_info["locale"]).get() ident = ( f"{price_request.currency}|{price_request.to_currency}|" f"{price_request_result.rate}|{price_request_result.last_trade_at}" ) results = [ InlineQueryResultArticle( id=ident, title=title, input_message_content=InputTextMessageContent( text_to, disable_web_page_preview=True, parse_mode=ParseMode.MARKDOWN, ), ) ] except (ValidationException, ConverterException): logging.info(f"inline_request unrecognized: {query}") results = [] finally: if len(query) <= settings.MAX_LEN_MSG_REQUESTS_LOG: write_request_log.delay( chat_id=update.effective_user.id, msg=query, created_at=datetime.now(), tag=f"Inline {tag}" if tag else "Inline", ) update.inline_query.answer(results)
def parse(self) -> PriceRequest: text = self.text obj = REQUEST_PATTERN_COMPILED.match(text) if not obj: raise WrongFormatException groups = obj.groups() if groups[PRICE_REQUEST_LEFT_AMOUNT] and groups[ PRICE_REQUEST_RIGHT_AMOUNT]: raise WrongFormatException if groups[PRICE_REQUEST_LEFT_AMOUNT]: direction_writing = DirectionWriting.LEFT2RIGHT elif groups[PRICE_REQUEST_RIGHT_AMOUNT]: direction_writing = DirectionWriting.RIGHT2LEFT else: direction_writing = DirectionWriting.UNKNOWN amount = groups[PRICE_REQUEST_LEFT_AMOUNT] or groups[ PRICE_REQUEST_RIGHT_AMOUNT] if amount: amount = parse_amount(amount, self.locale) text = groups[PRICE_REQUEST_CURRENCIES] text = text.upper() currencies = self.split_currencies(text) if len(currencies) == 2: currency, to_currency = currencies[0], currencies[1] if not self.is_currency_recognized( currency) or not self.is_currency_recognized(to_currency): raise UnknownCurrencyException elif len(currencies) == 1 and self.is_currency_recognized( currencies[0]): if self.default_currency_position: currency, to_currency = currencies[0], self.default_currency else: currency, to_currency = self.default_currency, currencies[0] if direction_writing == DirectionWriting.RIGHT2LEFT: currency, to_currency = to_currency, currency else: currencies = currencies[0] for x in range(2, len(currencies) - 1): currency, to_currency = currencies[:x], currencies[x:] if self.is_currency_recognized( currency) and self.is_currency_recognized(to_currency): break else: raise WrongFormatException if direction_writing == DirectionWriting.RIGHT2LEFT: currency, to_currency = to_currency, currency return PriceRequest( amount=amount, currency=currency, to_currency=to_currency, parser_name=self.name, direction_writing=direction_writing, )
def test_rounding(self): prr = PriceRequestResult( price_request=PriceRequest( amount=None, currency="USD", to_currency="EUR", parser_name="test-parser", direction_writing=DirectionWriting.UNKNOWN, ), exchanges=["test-exchanger"], rate=Decimal("0.5"), rate_open=Decimal("1"), low24h=Decimal("0.05"), high24h=Decimal("0.005"), last_trade_at=datetime.now(), ) fpr = FormatPriceRequestResult(prr, "en") self.assertTrue(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual( fpr.get(), "*USD EUR* 0.5 🔻\n-0.5 (-50.0%)\n*Low*: 0.05 *High*: 0.0050\n_17 March, 22:14 UTC_\ntest-exchanger 📡", ) prr = PriceRequestResult( price_request=PriceRequest( amount=None, currency="USD", to_currency="EUR", parser_name="test-parser", direction_writing=DirectionWriting.UNKNOWN, ), exchanges=["test-exchanger"], rate=Decimal("123456789012.123456789012"), rate_open=Decimal("1"), low24h=Decimal("0.000000000012"), high24h=Decimal("1"), last_trade_at=datetime.now(), ) fpr = FormatPriceRequestResult(prr, "en") self.assertTrue(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual( fpr.get(), """*USD EUR* 123,456,789,012.1235 ⬆️ +123,456,789,011.1235 (+12,345,678,901,112.35%) *Low*: 0.000000000012 *High*: 1.0 _17 March, 22:14 UTC_ test-exchanger 📡""", ) prr = PriceRequestResult( price_request=PriceRequest( amount=None, currency="USD", to_currency="EUR", parser_name="test-parser", direction_writing=DirectionWriting.UNKNOWN, ), exchanges=["test-exchanger"], rate=Decimal("123456789012.123456789012"), rate_open=Decimal("123456789000.000012345"), low24h=Decimal("1234567890.000"), high24h=Decimal("198765432112.000000000012"), last_trade_at=datetime.now(), ) fpr = FormatPriceRequestResult(prr, "en") self.assertTrue(fpr.is_rate_diff_available()) self.assertFalse(fpr.is_convert_mode()) self.assertEqual( fpr.get(), """*USD EUR* 123,456,789,012.1235 ⬆️ +12.1234 (+0.0000000098%) *Low*: 1,234,567,890.0 *High*: 198,765,432,112.0 _17 March, 22:14 UTC_ test-exchanger 📡""", )
def notification_checker() -> None: db_session = Session() pairs = (db_session.query( Notification.from_currency_id, Notification.to_currency_id).filter_by(is_active=true()).group_by( Notification.from_currency_id, Notification.to_currency_id).all()) for pair in pairs: from_currency = db_session.query(Currency).get(pair[0]) to_currency = db_session.query(Currency).get(pair[1]) if not from_currency.is_active or not to_currency.is_active: logging.info( "Disable notifications because currency %s %s is not active anymore", from_currency.code, to_currency.code, ) notification_auto_disable(pair) continue pr = PriceRequest( amount=None, currency=from_currency.code, to_currency=to_currency.code, parser_name="Notification", ) prr = convert(pr) notifications = (db_session.query(Notification).filter_by( is_active=true()).filter_by(from_currency_id=pair[0], to_currency_id=pair[1]).all()) for n in notifications: if is_triggered(n.trigger_clause, n.trigger_value, n.last_rate, prr.rate): # replace rate_open from daily to rate when was created or last triggered notification # TODO: PriceRequestResult -> dataclass, inside diff and etc nprr = PriceRequestResult( price_request=prr.price_request, exchanges=prr.exchanges, rate=prr.rate, rate_open=n.last_rate, last_trade_at=prr.last_trade_at, low24h=prr.low24h, high24h=prr.high24h, ) text_to = NotifyFormatPriceRequestResult(nprr, n.chat.locale).get() if n.trigger_clause in [ NotifyTriggerClauseEnum.less, NotifyTriggerClauseEnum.more, ]: n.is_active = False _ = get_translations(n.chat.locale) text_to += "\n" text_to += _("_One-time reminder. Set up a new reminder._") send_notification.delay(n.chat_id, text_to) n.last_rate = prr.rate transaction.commit()