1
0
PiggyBank/piggybank/currencies.py

149 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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
digits to the right are used to store a fraction part. You can simply divide
this number by 100 to get a floating-point number.
"""
from __future__ import annotations
from operator import mul
from typing import Dict, List, TypedDict
__all__ = [ "Currency", "CURRENCIES", "BaseCurrencyError",
"CurrencyIsNotSupportedError", "CurrenciesCoinCountMismatchError",
"CurrencyMismatchError"]
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
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)))}"
CURRENCIES: Dict[str, Currency] = {
"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.",
6, ["10к.", "50к.", "1₽", "2₽", "5₽", "10₽"],
[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,
["", "", "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])
}