Skip to content

Instantly share code, notes, and snippets.

@seblin
Last active July 12, 2019 22:09
Show Gist options
  • Save seblin/54e6e061b440e9be81de1c26ed1bb6c9 to your computer and use it in GitHub Desktop.
Save seblin/54e6e061b440e9be81de1c26ed1bb6c9 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
from decimal import Decimal
import re
from urllib.request import urlopen
from xml.etree import ElementTree as etree
EUROFXREF_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
XPATH_PATTERN = './/*[@currency]'
RATES = {}
_CONVERT_RE = r'\W*'.join([
r'(?P<amount>\d+(\.\d+)?)',
r'(?P<from_currency>[A-Z]{3})',
r'->',
r'(?P<to_currency>[A-Z]{3})',
])
def update_rates(url=EUROFXREF_URL, pattern=XPATH_PATTERN):
with urlopen(url) as stream:
tree = etree.parse(stream)
for elem in tree.iterfind(pattern):
RATES[elem.get('currency')] = Decimal(elem.get('rate'))
RATES['EUR'] = Decimal(1)
def get_rate(currency):
if not RATES:
update_rates()
if currency not in RATES:
raise ValueError(f'Unknown currency: {currency}')
return RATES[currency]
def get_currencies():
if not RATES:
update_rates()
return sorted(RATES.keys())
def convert(amount, from_currency, to_currency):
if from_currency != 'EUR':
# All rates refer to Euro
amount /= get_rate(from_currency)
return amount * get_rate(to_currency)
def convert_string(string, format_spec='.2f', show_currency=False):
match = re.match(_CONVERT_RE, string.strip().upper())
if not match:
raise ValueError('Invalid string format')
params = match.groupdict()
params['amount'] = Decimal(params['amount'])
result = format(convert(**params), format_spec)
if not show_currency:
return result
return f'{result} {params["to_currency"]}'
def main():
all_currencies = ', '.join(get_currencies())
print(f'Available currencies:\n{all_currencies}\n')
print(f'1 EUR equals {get_rate("USD")} US Dollar or {get_rate("JPY")} Yen')
print(f'42 US Dollar = {convert(42, "USD", "JPY"):.2f} Yen')
print(f'50 Euro = {convert_string("50 EUR -> CNY")} Chinese Yuan')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment