]> asedeno.scripts.mit.edu Git - bluechips.git/blob - bluechips/model/types.py
673a366927386ac8025821bd1ed1ce6adebe634e
[bluechips.git] / bluechips / model / types.py
1 """
2 Define special types used in BlueChips
3 """
4
5 import locale
6
7 import sqlalchemy as sa
8 from bluechips.lib.subclass import SmartSubclass
9
10 from weakref import WeakValueDictionary
11
12 def localeconv():
13     "Manually install en_US for systems that don't have it."
14     d = {'currency_symbol': '$',
15      'decimal_point': '.',
16      'frac_digits': 2,
17      'grouping': [3, 3, 0],
18      'int_curr_symbol': 'USD ',
19      'int_frac_digits': 2,
20      'mon_decimal_point': '.',
21      'mon_grouping': [3, 3, 0],
22      'mon_thousands_sep': ',',
23      'n_cs_precedes': 1,
24      'n_sep_by_space': 0,
25      'n_sign_posn': 1,
26      'negative_sign': '-',
27      'p_cs_precedes': 1,
28      'p_sep_by_space': 0,
29      'p_sign_posn': 1,
30      'positive_sign': '',
31      'thousands_sep': ','}
32     return d
33 locale.localeconv = localeconv
34
35 class Currency(object):
36     """
37     Store currency values as an integral number of cents
38     """
39     __metaclass__ = SmartSubclass(int)
40     __old_values__ = WeakValueDictionary()
41     def __new__(cls, value):
42         if value is None:
43             value = 0
44         elif isinstance(value, str):
45             value = int(float(value) * 100)
46         else:
47             value = int(value)
48         
49         if value not in cls.__old_values__:
50             new_object = super(cls, cls).__new__(cls)
51             new_object.value = value
52             cls.__old_values__[value] = new_object
53             return new_object
54         else:
55             return cls.__old_values__[value]
56     
57     def __int__(self):
58         """
59         If I don't define this, SmartSubclass will return
60         Currency(int(self.value))
61         """
62         return self.value
63     def __float__(self):
64         """
65         If I don't define this, SmartSubclass will return
66         Currency(float(self.value))
67         """
68         return float(self.value)
69     def __long__(self):
70         """
71         If I don't define this, SmartSubclass will return
72         Currency(long(self.value))
73         """
74         return long(self.value)
75     
76     def __cmp__(self, other):
77         """
78         This is overridden for when validators compare a Currency to
79         ''
80         """
81         if other == '':
82             return 1
83         else:
84             return self.value.__cmp__(int(other))
85     
86     def __mul__(self, other):
87         """
88         If I don't define this, SmartSubclass will convert the other
89         argument to an int
90         """
91         return Currency(self.value * other)
92     def __rmul__(self, other):
93         """
94         If I don't define this, SmartSubclass will convert the other
95         argument to an int
96         """
97         return self.__mul__(other)
98     
99     def __str_no_dollar__(self):
100         """
101         Get to the formatted string without the dollar sign
102         """
103         return str(self).replace('$', '')
104     
105     def __repr__(self):
106         return '%s("%s")' % (self.__class__.__name__, str(self))
107     def __str__(self):
108         return locale.currency(self.value / 100., grouping=True)
109
110 class DBCurrency(sa.types.TypeDecorator):
111     """
112     A type which represents monetary amounts internally as integers.
113     
114     This avoids binary/decimal float conversion issues
115     """
116     
117     impl = sa.types.Integer
118     
119     def process_bind_param(self, value, engine):
120         return int(value)
121     
122     def convert_result_value(self, value, engine):
123         return Currency(value)
124     process_result_value = convert_result_value