diff --git a/README.rst b/README.rst index 90d195c..430b50f 100644 --- a/README.rst +++ b/README.rst @@ -86,13 +86,37 @@ Usage Examples: >>> c.convert('USD', 'INR', 10) 674.73 +- Force use of Decimal + + .. code-block:: python + + python + >>> from forex_python.converter import CurrencyRates + >>> c = CurrencyRates(force_decimal=True) + >>> c.convert('USD', 'INR', Decimal('10.45')) + 705.09 + >>> c.convert('USD', 'INR', 10) + DecimalFloatMismatchError: convert requires amount parameter is of type Decimal when use_decimal=True + +- Detect use of Decimal + + .. code-block:: python + + python + >>> from forex_python.converter import CurrencyRates + >>> c = CurrencyRates() + >>> c.convert('USD', 'INR', Decimal('10.45')) + 705.09 + >>> c.convert('USD', 'INR', 10) + 674.73 + - Get latest Bitcoin price. .. code-block:: python python >>> from forex_python.bitcoin import BtcConverter - >>> b = BtcConverter() + >>> b = BtcConverter() # force_decimal=True to get Decimal rates >>> b.get_latest_price('USD') 533.913 diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 6322d16..76c6f4d 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -39,7 +39,7 @@ Currency Rates >>> from forex_python.converter import CurrencyRates >>> c = CurrencyRates(force_decimal=True) >>> c.convert('USD', 'INR', Decimal('10.45')) - 705.09 + Decimal('705.09') >>> c.convert('USD', 'INR', 10) DecimalFloatMismatchError: convert requires amount parameter is of type Decimal when use_decimal=True @@ -47,7 +47,7 @@ Currency Rates >>> from forex_python.converter import CurrencyRates >>> c = CurrencyRates() >>> c.convert('USD', 'INR', Decimal('10.45')) - 705.09 + Decimal('705.09') >>> c.convert('USD', 'INR', 10) 674.73 @@ -56,7 +56,7 @@ Bitcoin Prices: --------------- 1. Get latest price of one Bitcoin:: >>> from forex_python.bitcoin import BtcConverter - >>> b = BtcConverter() + >>> b = BtcConverter() # add "force_decimal=True" parmeter to get Decimal rates >>> b.get_latest_price('EUR') # you can directly call get_latest_price('EUR') 476.5225 # return type float @@ -94,7 +94,13 @@ Bitcoin Prices: >>> b.get_previous_price_list('INR', start_date, end_date) # get_previous_price_list('INR', start_date, end_date) {u'2016-05-19': 29371.7579, u'2016-05-18': 30402.3169, u'2016-05-22': 29586.3631, u'2016-05-23': 29925.3272, u'2016-05-20': 29864.0256, u'2016-05-21': 29884.7449} -8. Get Bitcoin symbol:: +8. Force use of Decimal:: + >>> from forex_python.bitcoin import BtcConverter + >>> b = BtcConverter(force_decimal=True) + >>> b.get_latest_price('EUR') # you can directly call get_latest_price('EUR') + Decimal('942.245000000000004547') # return type Decimal + +9. Get Bitcoin symbol:: >>> print(b.get_symbol()) # get_btc_symbol() ฿ diff --git a/forex_python/bitcoin.py b/forex_python/bitcoin.py index 0da234b..6ccd226 100644 --- a/forex_python/bitcoin.py +++ b/forex_python/bitcoin.py @@ -1,10 +1,22 @@ +from decimal import Decimal +import simplejson as json import requests +from .converter import RatesNotAvailableError, DecimalFloatMismatchError class BtcConverter(object): """ Get bit coin rates and convertion """ + def __init__(self, force_decimal=False): + self._force_decimal = force_decimal + + def _decode_rates(self, response, use_decimal=False): + if self._force_decimal or use_decimal: + decoded_data = json.loads(response.text, use_decimal=True) + else: + decoded_data = response.json() + return decoded_data def get_latest_price(self, currency): """ @@ -15,6 +27,8 @@ def get_latest_price(self, currency): if response.status_code == 200: data = response.json() price = data.get('bpi').get(currency, {}).get('rate_float', None) + if self._force_decimal: + return Decimal(price) return price return None @@ -34,8 +48,10 @@ def get_previous_price(self, currency, date_obj): if response.status_code == 200: data = response.json() price = data.get('bpi', {}).get(start, None) + if self._force_decimal: + return Decimal(price) return price - return None + raise RatesNotAvailableError("BitCoin Rates Source Not Ready For Given date") def get_previous_price_list(self, currency, start_date, end_date): """ @@ -51,7 +67,7 @@ def get_previous_price_list(self, currency, start_date, end_date): ) response = requests.get(url) if response.status_code == 200: - data = response.json() + data = self._decode_rates(response) price_dict = data.get('bpi', {}) return price_dict return {} @@ -60,36 +76,59 @@ def convert_to_btc(self, amount, currency): """ Convert X amount to Bit Coins """ + if isinstance(amount, Decimal): + use_decimal = True + else: + use_decimal = self._force_decimal + url = 'https://api.coindesk.com/v1/bpi/currentprice/{}.json'.format(currency) response = requests.get(url) if response.status_code == 200: data = response.json() price = data.get('bpi').get(currency, {}).get('rate_float', None) if price: - converted_btc = amount/price - return converted_btc - return None - return None + if use_decimal: + price = Decimal(price) + try: + converted_btc = amount/price + return converted_btc + except TypeError: + raise DecimalFloatMismatchError("convert_to_btc requires amount parameter is of type Decimal when force_decimal=True") + raise RatesNotAvailableError("BitCoin Rates Source Not Ready For Given date") def convert_btc_to_cur(self, coins, currency): """ Convert X bit coins to valid currency amount """ + if isinstance(coins, Decimal): + use_decimal = True + else: + use_decimal = self._force_decimal + url = 'https://api.coindesk.com/v1/bpi/currentprice/{}.json'.format(currency) response = requests.get(url) if response.status_code == 200: data = response.json() price = data.get('bpi').get(currency, {}).get('rate_float', None) if price: - converted_amount = coins * price - return converted_amount - return None - return None + if use_decimal: + price = Decimal(price) + try: + converted_amount = coins * price + return converted_amount + except TypeError: + raise DecimalFloatMismatchError("convert_btc_to_cur requires coins parameter is of type Decimal when force_decimal=True") + raise RatesNotAvailableError("BitCoin Rates Source Not Ready For Given date") def convert_to_btc_on(self, amount, currency, date_obj): """ Convert X amount to BTC based on given date rate """ + if isinstance(amount, Decimal): + use_decimal = True + else: + use_decimal = self._force_decimal + start = date_obj.strftime('%Y-%m-%d') end = date_obj.strftime('%Y-%m-%d') url = ( @@ -103,15 +142,24 @@ def convert_to_btc_on(self, amount, currency, date_obj): data = response.json() price = data.get('bpi', {}).get(start, None) if price: - converted_btc = amount/price - return converted_btc - return None - return None + if use_decimal: + price = Decimal(price) + try: + converted_btc = amount/price + return converted_btc + except TypeError: + raise DecimalFloatMismatchError("convert_to_btc_on requires amount parameter is of type Decimal when force_decimal=True") + raise RatesNotAvailableError("BitCoin Rates Source Not Ready For Given Date") def convert_btc_to_cur_on(self, coins, currency, date_obj): """ Convert X BTC to valid currency amount based on given date """ + if isinstance(coins, Decimal): + use_decimal = True + else: + use_decimal = self._force_decimal + start = date_obj.strftime('%Y-%m-%d') end = date_obj.strftime('%Y-%m-%d') url = ( @@ -125,14 +173,18 @@ def convert_btc_to_cur_on(self, coins, currency, date_obj): data = response.json() price = data.get('bpi', {}).get(start, None) if price: - converted_btc = coins*price - return converted_btc - return None - return None + if use_decimal: + price = Decimal(price) + try: + converted_btc = coins*price + return converted_btc + except TypeError: + raise DecimalFloatMismatchError("convert_btc_to_cur_on requires amount parameter is of type Decimal when force_decimal=True") + raise RatesNotAvailableError("BitCoin Rates Source Not Ready For Given Date") def get_symbol(self): """ - Here is Unicode symbol for bit coin + Here is Unicode symbol for bitcoin """ return "\u0E3F" diff --git a/forex_python/converter.py b/forex_python/converter.py index fff0102..774e294 100644 --- a/forex_python/converter.py +++ b/forex_python/converter.py @@ -1,7 +1,7 @@ import os +from decimal import Decimal import simplejson as json import requests -from decimal import Decimal class RatesNotAvailableError(Exception): diff --git a/tests/test.py b/tests/test.py index f6fd0a4..b025218 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,9 +1,9 @@ import datetime +from decimal import Decimal from unittest import TestCase from forex_python.converter import (get_rates, get_rate, convert, get_symbol, get_currency_name, RatesNotAvailableError, CurrencyRates, DecimalFloatMismatchError) -from decimal import Decimal class TestGetRates(TestCase): diff --git a/tests/test_bitcoin.py b/tests/test_bitcoin.py index c6cd341..a747c8c 100644 --- a/tests/test_bitcoin.py +++ b/tests/test_bitcoin.py @@ -1,16 +1,12 @@ import datetime +from decimal import Decimal from unittest import TestCase -from forex_python.bitcoin import * +from forex_python.bitcoin import (get_btc_symbol, convert_btc_to_cur_on, convert_to_btc_on, + convert_btc_to_cur, convert_to_btc, get_latest_price, + get_previous_price, get_previous_price_list, BtcConverter) +from forex_python.converter import RatesNotAvailableError, DecimalFloatMismatchError -class TestCommon(TestCase): - """ - Common class with setUp method for all test cases - """ - def setUp(self): - self.b = BtcConverter() - - class TestLatestPrice(TestCase): """ Test get latest price using currency code @@ -35,8 +31,7 @@ def test_previous_price_valid_currency(self): def test_previous_price_invalid_currency(self): date_obj = datetime.datetime.today() - datetime.timedelta(days=15) - price = get_previous_price('XYZ', date_obj) - self.assertFalse(price) + self.assertRaises(RatesNotAvailableError, get_previous_price, 'XYZ', date_obj) class TestPreviousPriceList(TestCase): @@ -67,8 +62,7 @@ def test_convet_to_btc_with_valid_currency(self): self.assertEqual(type(coins), float) def test_convet_to_btc_with_invalid_currency(self): - coins = convert_to_btc(250, 'XYZ') - self.assertFalse(coins) + self.assertRaises(RatesNotAvailableError, convert_to_btc, 250, 'XYZ') class TestConvertBtcToCur(TestCase): @@ -80,8 +74,7 @@ def test_convert_btc_to_cur_valid_currency(self): self.assertEqual(type(amount), float) def test_convert_btc_to_cur_invalid_currency(self): - amount = convert_btc_to_cur(2, 'XYZ') - self.assertFalse(amount) + self.assertRaises(RatesNotAvailableError, convert_btc_to_cur, 2, 'XYZ') class TestConvertToBtcOn(TestCase): @@ -95,8 +88,7 @@ def test_convert_to_btc_on_with_valid_currency(self): def test_convert_to_btc_on_with_invalid_currency(self): date_obj = datetime.datetime.today() - datetime.timedelta(days=15) - coins = convert_to_btc_on(300, 'XYZ', date_obj) - self.assertFalse(coins) + self.assertRaises(RatesNotAvailableError, convert_to_btc_on, 300, 'XYZ', date_obj) class TestConvertBtcToCurOn(TestCase): @@ -110,8 +102,7 @@ def test_convert_to_btc_on_with_valid_currency(self): def test_convert_to_btc_on_with_invalid_currency(self): date_obj = datetime.datetime.today() - datetime.timedelta(days=15) - amount = convert_btc_to_cur_on(3, 'XYZ', date_obj) - self.assertFalse(amount) + self.assertRaises(RatesNotAvailableError, convert_btc_to_cur_on, 3, 'XYZ', date_obj) class TestBitCoinSymbol(TestCase): @@ -120,3 +111,142 @@ class TestBitCoinSymbol(TestCase): """ def test_bitcoin_symbol(self): self.assertEqual(get_btc_symbol(), "\u0E3F") + + +class TestBitCoinWithoutForceDecimal(TestCase): + + def setUp(self): + self.b = BtcConverter() + + def test_latest_price_valid_currency(self): + price = self.b.get_latest_price('USD') + self.assertEqual(type(price), float) + + def test_latest_price_invalid_currency(self): + price = self.b.get_latest_price('XYZ') + self.assertFalse(price) + + def test_previous_price_valid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + price = self.b.get_previous_price('USD', date_obj) + self.assertEqual(type(price), float) + + def test_previous_price_invalid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + self.assertRaises(RatesNotAvailableError, self.b.get_previous_price, 'XYZ', date_obj) + + def test_previous_price_list_with_valid_currency(self): + start_date = datetime.datetime.today() - datetime.timedelta(days=15) + end_date = datetime.datetime.today() + price_list = self.b.get_previous_price_list('USD', start_date, end_date) + self.assertTrue(price_list) + self.assertEqual(type(price_list), dict) + + def test_previous_price_list_with_invalid_currency(self): + start_date = datetime.datetime.today() - datetime.timedelta(days=15) + end_date = datetime.datetime.today() + price_list = self.b.get_previous_price_list('XYZ', start_date, end_date) + self.assertFalse(price_list) + self.assertEqual(type(price_list), dict) + + def test_convet_to_btc_with_valid_currency(self): + coins = self.b.convert_to_btc(250, 'USD') + self.assertEqual(type(coins), float) + + def test_convet_to_btc_with_invalid_currency(self): + self.assertRaises(RatesNotAvailableError, self.b.convert_to_btc, 250, 'XYZ') + + def test_convert_btc_to_cur_valid_currency(self): + amount = self.b.convert_btc_to_cur(2, 'USD') + self.assertEqual(type(amount), float) + + def test_convert_btc_to_cur_invalid_currency(self): + self.assertRaises(RatesNotAvailableError, self.b.convert_btc_to_cur, 2, 'XYZ') + + def test_convert_to_btc_on_with_valid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + coins = self.b.convert_to_btc_on(300, 'USD', date_obj) + self.assertEqual(type(coins), float) + + def test_convert_to_btc_on_with_invalid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + self.assertRaises(RatesNotAvailableError, self.b.convert_to_btc_on, 300, 'XYZ', date_obj) + + def test_convert_to_btc_on_with_valid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + amount = self.b.convert_btc_to_cur_on(3, 'USD', date_obj) + self.assertEqual(type(amount), float) + + def test_convert_to_btc_on_with_invalid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + self.assertRaises(RatesNotAvailableError, self.b.convert_btc_to_cur_on, 3, 'XYZ', date_obj) + + +class TestBitCoinForceDecimal(TestCase): + + def setUp(self): + self.b = BtcConverter(force_decimal=True) + + def test_latest_price_valid_currency(self): + price = self.b.get_latest_price('USD') + self.assertEqual(type(price), Decimal) + + def test_latest_price_invalid_currency(self): + price = self.b.get_latest_price('XYZ') + self.assertFalse(price) + + def test_previous_price_valid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + price = self.b.get_previous_price('USD', date_obj) + self.assertEqual(type(price), Decimal) + + def test_previous_price_invalid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + self.assertRaises(RatesNotAvailableError, self.b.get_previous_price, 'XYZ', date_obj) + + def test_previous_price_list_with_valid_currency(self): + start_date = datetime.datetime.today() - datetime.timedelta(days=15) + end_date = datetime.datetime.today() + price_list = self.b.get_previous_price_list('USD', start_date, end_date) + self.assertTrue(price_list) + self.assertEqual(type(price_list), dict) + + def test_previous_price_list_with_invalid_currency(self): + start_date = datetime.datetime.today() - datetime.timedelta(days=15) + end_date = datetime.datetime.today() + price_list = self.b.get_previous_price_list('XYZ', start_date, end_date) + self.assertFalse(price_list) + self.assertEqual(type(price_list), dict) + + def test_convet_to_btc_with_valid_currency(self): + coins = self.b.convert_to_btc(Decimal('250'), 'USD') + self.assertEqual(type(coins), Decimal) + + def test_convet_to_btc_with_invalid_currency(self): + self.assertRaises(RatesNotAvailableError, self.b.convert_to_btc, Decimal('250'), 'XYZ') + + def test_convert_btc_to_cur_valid_currency(self): + amount = self.b.convert_btc_to_cur(Decimal('2'), 'USD') + self.assertEqual(type(amount), Decimal) + + def test_convert_btc_to_cur_invalid_currency(self): + self.assertRaises(RatesNotAvailableError, self.b.convert_btc_to_cur, Decimal('250'), 'XYZ') + + def test_convert_to_btc_on_with_valid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + coins = self.b.convert_to_btc_on(Decimal('300'), 'USD', date_obj) + self.assertEqual(type(coins), Decimal) + + def test_convert_to_btc_on_with_invalid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + self.assertRaises(RatesNotAvailableError, self.b.convert_to_btc_on, Decimal('250'), 'XYZ', date_obj) + + def test_convert_to_btc_on_with_valid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + amount = self.b.convert_btc_to_cur_on(Decimal('250'), 'USD', date_obj) + self.assertEqual(type(amount), Decimal) + + def test_convert_to_btc_on_with_invalid_currency(self): + date_obj = datetime.datetime.today() - datetime.timedelta(days=15) + self.assertRaises(RatesNotAvailableError, self.b.convert_btc_to_cur_on, Decimal('3'), 'XYZ', date_obj) +