2013/7/16 Cédric Krier <[email protected]> > On 16/07/13 12:12 +0300, Giedrius Slavinskas wrote: > > 2013/7/15 Cédric Krier <[email protected]> > > > > > On 15/07/13 10:25 +0300, Giedrius Slavinskas wrote: > > > So let's introduce it. > > > > > > > > > class Measure(namedtuple('Measure', ['quantity', 'unit'])): > > > __slots__ = () > > > > > > def convert_to(self, uom, round=True): > > > Uom = Pool().get('product.uom') > > > return Measure(Uom.compute_qty(self.unit, self.quantity, uom, > > > round=round), uom) > > > > > > > > > Which will make: > > > > > > Measure(1000, gr).convert_to(kg) == Measure(1, kg) > > > > > > I suggest more intuitive interface/naming. Here is just the idea, > > nothing mean to work. > > > > class Quantity(namedtuple('Quantity', ['units', 'uom'])): > > I don't understand why using "units" ? For me, it sounds wrong. > unit is what is called uom == Unit of Measure. > > Quantity sounds like it is a scalar, when measure sounds better > especially because we already use "Unit of Measure". > > > __slots__ = () > > > > def convert(self, to_uom, round=False): # round=False is more common > > usage > > Aggre about the round because it should be done most of the time at the > end (see below). > But the name convert_to is more explicite. >
I agree, that "convert" is a bit confusing as if the name of instance method means an action, it is understood that the method will change the instance itself (but not return the new object). Here is implemented the same approach http://techspot.zzzeek.org/files/2011/account_currency.py. We can borrow the naming of convert method, so it could be "as_measure()" ex. internal_quantity = move.quantity.as_measure(move.product.default_uom) > > > Uom = Pool().get('product.uom') > > return Quantity(Uom.compute_qty(self.uom, self.units, to_uom, > > round=round), to_uom) > > > > def __add__(self, other): # same for __sub__, __mul__, etc > > if isinstance(other, Quantity): > > other = other.convert(self.uom, round=False).units > > return Quantity(self.units + other, self.uom) > > This makes the addition not commutative. > > > def __eq__(self, other): # same for __le__, __gt__, etc > > if isinstance(other, Quantity): > > other = other.convert(self.uom, round=False).units > > return self.units == other > > > > def round(self): > > # round self.units by self.uom.digits > > > > Then such class could be used as field on model: > > > > class StockMove: > > units = fields.Float("Quantity") > > uom = fields.Many2One("product.uom", "Uom") > > > > # changing the quantity it changes units and uom and vice versa. > > # This field should not hold values, but only references. > > quantity = fields.Function(Quantity('units', 'uom')) > > > > internal_units = fields.Float('Internal Quantity', readonly=True, > > required=True) > > internal_quantity = fields.Function(Quantity('internal_units', > > 'product.default_uom')) > > > > def do_something(self, moves): > > """Usage example""" > > for move in moves: > > self.quantity += move.quantity * 2 > > This must not be a function field but a property or a descriptor. > The descriptor could be set with a classmethod like: > > measure = Measure.descriptor('quantity', 'unit') > Basic idea is that Measure can be reused on any module and the reference to uom can be linked from other object. > > The setter should take care of rounding the quantity before storing it > on the instance. Yes, this is one more benefit of having such interface/type. -- Giedrius
