2020-06-06 03:00:36 +04:00
|
|
|
|
"""Dictionary of supported currencies as instances of a Currency class.
|
|
|
|
|
|
|
|
|
|
Face values are stored as decimals*.
|
|
|
|
|
|
|
|
|
|
* Decimal is used to avoid problems of rounding float numbers. So first two
|
2020-03-27 20:14:22 +04:00
|
|
|
|
digits to the right are used to store a fraction part. You can simply divide
|
2020-06-06 03:00:36 +04:00
|
|
|
|
this number by 100 to get a floating-point number.
|
2019-12-25 23:08:20 +04:00
|
|
|
|
"""
|
|
|
|
|
|
2020-06-06 03:00:36 +04:00
|
|
|
|
from __future__ import annotations
|
|
|
|
|
from operator import mul
|
2019-12-26 00:00:59 +04:00
|
|
|
|
from typing import Dict, List, TypedDict
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
2020-06-06 03:00:36 +04:00
|
|
|
|
__all__ = [ "Currency", "CURRENCIES", "BaseCurrencyError",
|
|
|
|
|
"CurrencyIsNotSupportedError", "CurrenciesCoinCountMismatchError",
|
|
|
|
|
"CurrencyMismatchError"]
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BaseCurrencyError(Exception):
|
|
|
|
|
"""Base class for all currency exeptions."""
|
|
|
|
|
def __init__(self, message=None, *args, **kwargs) -> None:
|
|
|
|
|
if message is None:
|
|
|
|
|
message = self.__doc__
|
|
|
|
|
super().__init__(message, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CurrencyIsNotSupportedError(BaseCurrencyError):
|
|
|
|
|
"""Currency is not supported."""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CurrencyMismatchError(BaseCurrencyError):
|
|
|
|
|
"""Currencies doesn't match, but they must do so."""
|
|
|
|
|
def __init__(self, extra=None):
|
|
|
|
|
if not extra is None:
|
|
|
|
|
super().__init__(f"{self.__doc__} {extra}")
|
|
|
|
|
else:
|
|
|
|
|
super().__init__(self.__doc__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CurrenciesCoinCountMismatchError(BaseCurrencyError):
|
|
|
|
|
"""Count of coins of a new currency and an old one should be equal."""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
2020-06-06 03:00:36 +04:00
|
|
|
|
class Currency:
|
|
|
|
|
"""Currency's representation and methods to work with it.
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
iso_code -- ISO code of a currency;
|
|
|
|
|
name -- full name of a currency;
|
|
|
|
|
description -- description of a currency;
|
|
|
|
|
coins_count -- number of face values of coins;
|
|
|
|
|
coin_names -- list of names of face values (e.g. 1c, 5c);
|
|
|
|
|
face_values -- list of values for each face value in decimal
|
|
|
|
|
(e.g. $476.40 is 47640);
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self, iso_code: str, name: str, description: str,
|
|
|
|
|
coins_count: int, coin_names: List[str],
|
|
|
|
|
face_values: List[int]):
|
|
|
|
|
self._iso = iso_code.upper()
|
|
|
|
|
self._name = name
|
|
|
|
|
self._description = description
|
|
|
|
|
self._coins_count = coins_count
|
|
|
|
|
self._coin_names = coin_names
|
|
|
|
|
self._face_values = face_values
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def iso(self) -> str:
|
|
|
|
|
"""ISO code of a currency."""
|
|
|
|
|
return self._iso
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def name(self) -> str:
|
|
|
|
|
"""Full name of a currency."""
|
|
|
|
|
return self._name
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def description(self) -> str:
|
|
|
|
|
"""Description of a currency (e.g. country)."""
|
|
|
|
|
return self._description
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def count(self):
|
|
|
|
|
"""Count of coins of a currency."""
|
|
|
|
|
return self._coins_count
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def coin_names(self) -> List[str]:
|
|
|
|
|
"""Names of coins (usually face values, e.g. 25¢).
|
|
|
|
|
From least to most."""
|
|
|
|
|
return self._coin_names
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def face_values(self) -> List[int]:
|
|
|
|
|
"""Face values of coins. From least to most."""
|
|
|
|
|
return self._face_values
|
|
|
|
|
|
|
|
|
|
def multiply(self, lst: List[int]) -> List[float]:
|
|
|
|
|
"""Multiplies a list by face values. Length of list must match
|
|
|
|
|
currency's coins count."""
|
|
|
|
|
if len(lst) != len(self):
|
|
|
|
|
raise CurrenciesCoinCountMismatchError
|
|
|
|
|
return list(map(mul, lst, self.face_values))
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def from_string(currency: str) -> Currency:
|
|
|
|
|
"""Creates a Currency object from its string representation."""
|
|
|
|
|
iso, name, desc, ccnt, fvn, fv = currency.split(";")
|
|
|
|
|
return Currency(iso, name, desc, int(ccnt),
|
|
|
|
|
fvn.split(","), list(map(int, fv.split(","))))
|
|
|
|
|
|
|
|
|
|
def __mul__(self, lst: List[int]) -> List[float]:
|
|
|
|
|
return self.multiply(lst)
|
|
|
|
|
|
|
|
|
|
def __len__(self) -> int:
|
|
|
|
|
return self.count
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return f"{self.iso};{self.name};{self.description};" \
|
|
|
|
|
f"{self.count};{','.join(self.coin_names)};" \
|
|
|
|
|
f"{','.join(list(map(str, self.face_values)))}"
|
2019-12-26 00:00:59 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CURRENCIES: Dict[str, Currency] = {
|
2020-06-06 03:00:36 +04:00
|
|
|
|
"RUB": Currency("RUB", "Russian ruble", "Russian Federation", 8,
|
|
|
|
|
["1к.", "5к.", "10к.", "50к.", "1₽", "2₽", "5₽", "10₽"],
|
|
|
|
|
[1, 5, 10, 50, 1_00, 2_00, 5_00, 10_00]),
|
|
|
|
|
"SRUB": Currency("SRUB", "Russian ruble (shortened)",
|
|
|
|
|
"Russian Federation. Excluding coins of 1 and 5 kopek.",
|
2020-06-07 04:47:15 +04:00
|
|
|
|
6, ["10к.", "50к.", "1₽", "2₽", "5₽", "10₽"],
|
2020-06-06 03:00:36 +04:00
|
|
|
|
[10, 50, 1_00, 2_00, 5_00, 10_00]),
|
|
|
|
|
"BYN": Currency("BYN", "Belarusian ruble", "Belarus", 8,
|
|
|
|
|
["1к.", "2к.", "5к.", "10к.", "20к.", "50к.", "1р.", "2р."],
|
|
|
|
|
[1, 2, 5, 10, 20, 50, 1_00, 2_00]),
|
|
|
|
|
"UAH": Currency("UAH", "Ukrainian hryvnia", "Ukraine", 10,
|
|
|
|
|
["1к.", "2к.", "5к.", "10к.", "25к.", "50к.", "₴1", "₴2", "₴5", "₴10"],
|
|
|
|
|
[1, 2, 5, 10, 25, 50, 1_00, 2_00, 5_00, 10_00]),
|
|
|
|
|
"USD": Currency("USD", "US Dollar", "United States of America", 6,
|
|
|
|
|
["1¢", "5¢", "10¢", "25¢", "50¢", "$1"],
|
|
|
|
|
[1, 5, 10, 25, 50, 1_00]),
|
|
|
|
|
"EUR": Currency("EUR", "Euro", "European Union", 8,
|
|
|
|
|
["1c", "2c", "5c", "10c", "20c", "50c", "€1", "€2"],
|
|
|
|
|
[1, 2, 5, 10, 20, 50, 1_00, 2_00]),
|
|
|
|
|
"GBP": Currency("GBP", "Pound sterling", "United Kingdom", 9,
|
|
|
|
|
["1p", "2p", "5p", "10p", "20p", "25p", "50p", "£1", "£2"],
|
|
|
|
|
[1, 2, 5, 10, 20, 25, 50, 1_00, 2_00])
|
2019-12-25 23:08:20 +04:00
|
|
|
|
}
|