X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=bluechips%2Fmodel%2Ftypes.py;h=8cea5e8b107d781fe716e26f572f4eda3694bdc5;hb=c77b0b514f4b527412c9d1ad4613029f48fe73de;hp=4de0033c3a2b8b86c7256210b8f13ef655382567;hpb=ca60cb7549b8610ed076fb059b556f8c6ffd9b4e;p=bluechips.git diff --git a/bluechips/model/types.py b/bluechips/model/types.py index 4de0033..8cea5e8 100644 --- a/bluechips/model/types.py +++ b/bluechips/model/types.py @@ -2,19 +2,84 @@ Define special types used in BlueChips """ +import locale +from decimal import Decimal, InvalidOperation + import sqlalchemy as sa +from formencode import validators, Invalid from bluechips.lib.subclass import SmartSubclass +from weakref import WeakValueDictionary + +def localeconv(): + "Manually install en_US for systems that don't have it." + d = {'currency_symbol': '$', + 'decimal_point': '.', + 'frac_digits': 2, + 'grouping': [3, 3, 0], + 'int_curr_symbol': 'USD ', + 'int_frac_digits': 2, + 'mon_decimal_point': '.', + 'mon_grouping': [3, 3, 0], + 'mon_thousands_sep': ',', + 'n_cs_precedes': 1, + 'n_sep_by_space': 0, + 'n_sign_posn': 1, + 'negative_sign': '-', + 'p_cs_precedes': 1, + 'p_sep_by_space': 0, + 'p_sign_posn': 1, + 'positive_sign': '', + 'thousands_sep': ','} + return d +locale.localeconv = localeconv + + +class CurrencyValidator(validators.FancyValidator): + "A validator to convert to Currency objects." + messages = {'amount': "Please enter a valid currency amount", + 'precision': "Only two digits after the decimal, please", + 'nonzero': "Please enter a non-zero amount"} + + def _to_python(self, value, state): + try: + dec = Decimal(value) + except InvalidOperation: + raise Invalid(self.message('amount', state), + value, state) + else: + ret = dec.quantize(Decimal('1.00')) + if ret == 0: + raise Invalid(self.message('nonzero', state), + value, state) + elif ret != dec: + raise Invalid(self.message('precision', state), + value, state) + else: + return Currency(int(ret * 100)) + + class Currency(object): """ Store currency values as an integral number of cents """ __metaclass__ = SmartSubclass(int) - def __init__(self, value): - if isinstance(value, str): - self.value = int(float(value) * 100) + __old_values__ = WeakValueDictionary() + def __new__(cls, value): + if value is None: + value = 0 + elif isinstance(value, str): + value = int(float(value) * 100) + else: + value = int(value) + + if value not in cls.__old_values__: + new_object = super(cls, cls).__new__(cls) + new_object.value = value + cls.__old_values__[value] = new_object + return new_object else: - self.value = int(value) + return cls.__old_values__[value] def __int__(self): """ @@ -58,19 +123,11 @@ class Currency(object): """ return self.__mul__(other) - def __str_no_dollar__(self): - """ - Get to the formatted string without the dollar sign - """ - return str(self)[1:] - def __repr__(self): return '%s("%s")' % (self.__class__.__name__, str(self)) def __str__(self): - sign = '-' if self.value < 0 else '' - cents = abs(self.value) % 100 - dollars = (abs(self.value) - cents) / 100 - return '$%s%s.%.02d' % (sign, dollars, cents) + return locale.currency(self.value / 100., grouping=True) + class DBCurrency(sa.types.TypeDecorator): """ @@ -86,3 +143,4 @@ class DBCurrency(sa.types.TypeDecorator): def convert_result_value(self, value, engine): return Currency(value) + process_result_value = convert_result_value