# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
##
## Copyright (C) 2008-2013 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
import datetime
import decimal
import warnings
from lxml import etree
from kiwi.currency import currency
from storm.properties import RawStr, Int, Bool, DateTime, Decimal, Unicode
from storm.properties import SimpleProperty
from storm.store import AutoReload
from storm.variables import (DateVariable, DateTimeVariable,
DecimalVariable, IntVariable,
Variable, EncodedValueVariable)
from stoqlib.lib.defaults import QUANTITY_PRECISION
[docs]class Identifier(int):
def __new__(cls, value):
instance = super(Identifier, cls).__new__(cls, value)
instance.prefix = ''
return instance
def __str__(self):
return '%s%05d' % (self.prefix, self)
def __unicode__(self):
return unicode(str(self))
class _IdentifierVariable(IntVariable):
def parse_get(self, value, to_db):
return Identifier(value)
[docs]class IdentifierCol(Int):
"""A numeric identifier for an object
This should be using when defining an identifier column to have
some facilities, like formating it to a predefined pattern when
converted to str/unicode. For instance::
>>> from stoqlib.domain.base import Domain
>>> from stoqlib.database.runtime import new_store
>>>
>>> class TestProduct(Domain):
... identifier = IdentifierCol()
>>>
>>> store = new_store()
>>> p = TestProduct(store=store)
>>> p.identifier = 666
>>>
>>> p.identifier
666
>>> str(p.identifier)
'00666'
>>> unicode(p.identifier)
u'00666'
>>>
>>> store.rollback(close=True)
"""
variable_class = _IdentifierVariable
def __init__(self, primary=False):
super(IdentifierCol, self).__init__(default=AutoReload, primary=primary)
def __get__(self, obj, cls=None):
# This will get the column definition or the variable
data = super(IdentifierCol, self).__get__(obj, cls)
# if there is an object, then its the variable
if obj and hasattr(obj, 'branch'):
data.prefix = obj.branch.acronym or ''
return data
[docs]class PriceVariable(DecimalVariable):
[docs] def parse_set(self, value, from_db):
# XXX: We cannot reduce the precision when converting to currency, since
# sometimes we need a cost of a product to have more than 2 digits
return currency(DecimalVariable.parse_set(value, from_db))
[docs]class PriceCol(Decimal):
variable_class = PriceVariable
[docs]class QuantityVariable(DecimalVariable):
[docs] def parse_set(self, value, from_db):
return decimal.Decimal('%0.*f' % (QUANTITY_PRECISION, value))
[docs]class QuantityCol(Decimal):
variable_class = QuantityVariable
[docs]class PercentCol(Decimal):
pass
[docs]class EnumVariable(Variable):
__slots__ = ()
[docs] def parse_set(set, value, from_db):
return value
[docs] def parse_get(self, value, to_db):
if isinstance(value, str):
value = unicode(value)
return value
[docs]class EnumCol(SimpleProperty):
variable_class = EnumVariable
[docs]class MyDateTimeVariable(DateTimeVariable, DateVariable):
[docs] def parse_set(self, value, from_db):
if type(value) is datetime.date:
warnings.warn("Using datetime.date is deprecated, pass in "
"datetime.datetime instead", stacklevel=4)
value = datetime.datetime(value.year, value.month, value.day)
return DateTimeVariable.parse_set(self, value, from_db)
[docs]class DateTimeCol(DateTime):
variable_class = MyDateTimeVariable
[docs]class UUIDVariable(Variable):
__slots__ = ()
# FIXME: There's a cache invalidation bug in Storm that breaks
# test_transaction.testCacheInvalidation that prevents us from
# being able to use uuid instances
[docs] def parse_set(self, value, from_db):
# if from_db and isinstance(value, basestring):
# value = uuid.UUID(value)
# elif not isinstance(value, uuid.UUID):
# raise TypeError("Expected UUID, found %r: %r"
# % (type(value), value))
return unicode(value)
[docs]class UUIDCol(SimpleProperty):
variable_class = UUIDVariable
[docs]class XmlVariable(EncodedValueVariable):
def _loads(self, value):
return etree.fromstring(value)
def _dumps(self, value):
if value is None:
return None
return unicode(etree.tostring(value, encoding='unicode'))
[docs]class XmlCol(SimpleProperty):
variable_class = XmlVariable
# Columns, we're keeping the Col suffix to avoid clashes between
# decimal.Decimal and storm.properties.Decimal
BLOBCol = RawStr
BoolCol = Bool
DecimalCol = Decimal
IdCol = UUIDCol
IntCol = Int
UnicodeCol = Unicode