source: main/waeup.sirp/branches/ulif-fasttables/src/waeup/sirp/utils/converters.py @ 11105

Last change on this file since 11105 was 5317, checked in by uli, 14 years ago

Accept also lowercase token strings when converting choice tokens.

File size: 7.7 KB
Line 
1"""Converters for zope.schema-based datatypes.
2"""
3import datetime
4import grok
5from zope.component import getMultiAdapter
6from zope.publisher.browser import TestRequest
7try:
8    from zope.app.form.browser.interfaces import ITerms
9except ImportError:
10    from zope.browser.interfaces import ITerms
11from zope.schema.interfaces import IBool, IText, IInt, IChoice, IDate
12from waeup.sirp.interfaces import ISchemaTypeConverter
13
14# If a string has this value, it is considered as 'missing_value' or None.
15NONE_STRING_VALUE = ''
16
17class Converter(grok.Adapter):
18    """Base for string-or-none to zope.schema field converters.
19    """
20    grok.baseclass()
21    grok.provides(ISchemaTypeConverter)
22   
23    def __init__(self, context):
24        """Create a converter with context, a zope.schema.Field, as context.
25        """
26        self.context = context
27
28    def _convertValueFromString(self, string):
29        """You must at least override this method to build a working
30           converter.
31        """
32        raise NotImplementedError('method not implemented')
33   
34    def fromString(self, string=None, strict=True):
35        """Convert ``string`` to value according to assigned field type.
36        """
37        result = None
38        if string is NONE_STRING_VALUE:
39            string = None
40        if string is None:
41            if self.context.required is True:
42                result = self.context.default
43            else:
44                result = self.context.missing_value
45        else:
46            result = self._convertValueFromString(string)
47        if strict:
48            self.context.validate(result)
49        return result
50
51    def _convertValueToString(self, value):
52        return str(value)
53
54    def toString(self, value, strict=True):
55        """Convert given `value` to string according to assigned field type.
56        """
57        if strict:
58            self.context.validate(value)
59        if value == self.context.missing_value:
60            return None
61        return self._convertValueToString(value)
62
63class BoolConverter(Converter):
64    """A converter for zope.schema.Bool fields.
65    """
66    grok.context(IBool)
67    grok.provides(ISchemaTypeConverter)
68
69    def _convertValueFromString(self, string):
70        if string is NONE_STRING_VALUE:
71            string = None
72        if string is None:
73            return None
74        if string.lower() in ['1', 'true', 'yes']:
75            return True
76        return False
77
78    def _convertValueToString(self, value):
79        if value is None:
80            return None
81        if value:
82            return '1'
83        return '0'
84   
85class TextConverter(Converter):
86    """A converter for zope.schema.interfaces.IText fields.
87    """
88    grok.context(IText)
89    grok.provides(ISchemaTypeConverter)
90
91    def _convertValueFromString(self, string):
92        return unicode(string)
93
94class IntConverter(Converter):
95    """A converter for zope.schema.Int fields.
96    """
97    grok.context(IInt)
98    grok.provides(ISchemaTypeConverter)
99
100    def _convertValueFromString(self, string):
101        return int(string)
102
103class ChoiceConverter(Converter):
104    """A converter for zope.schema.Choice fields.
105    """
106    grok.context(IChoice)
107    grok.provides(ISchemaTypeConverter)
108
109    tokens = None
110    values = None
111   
112    def __init__(self, context):
113        self.context = context
114        if not hasattr(self.context.source, 'factory'):
115            try:
116                self.terms = getMultiAdapter(
117                    (self.context.source, TestRequest()), ITerms)
118            except:
119                self.terms = None
120
121            return
122        if not hasattr(self.context.source.factory, 'getToken'):
123            return
124        # For expensive token/key lookups we create a 'cache'
125        # here. This speeds up mass operations with many conversions
126        # by factor 10 or more.
127       
128        # Mapping token -> value
129        self.tokens = dict([(self.context.source.factory.getToken(x), x)
130                            for x in self.context.source.factory.getValues()])
131        # Mapping value -> token
132        self.values = dict([(y,x) for x,y in self.tokens.items()])
133
134    def _convertValueFromString(self, string):
135        if self.tokens is not None:
136            result = None
137            try:
138                result = self.tokens[string]
139            except KeyError:
140                # Be gentle...
141                try:
142                    result = self.tokens[string.lower()]
143                    return result
144                except KeyError:
145                    tokenlist = (','.join(self.tokens[:2]))
146                    raise ValueError(
147                        'The token %s is not valid. Use one of %s, ...' % (
148                            string, tokenlist))
149            return result
150        if self.terms is not None:
151            return self.terms.getValue(string)
152        result = self.context.source.getTermByToken(string).value
153        return result
154
155    def _convertValueToString(self, value):
156        if self.values is not None:
157            return self.values[value]
158        if self.terms is not None:
159            return self.terms.getTerm(value).token
160        return str(value)
161
162    def fromString(self, string=None, strict=False):
163        """Convert ``string`` to value according to assigned field type.
164
165        We change the default for ``strict``: this disables extra
166        validation checks for Choice fields and saves lots of time. If
167        a string/value is out of allowed range we get a value or key
168        error anyway.
169        """
170        return super(ChoiceConverter, self).fromString(string=string,
171                                                       strict=strict)
172
173    def toString(self, value, strict=False):
174        """Convert ``value`` to string according to assigned field type.
175
176        We change the default for ``strict``: this disables extra
177        validation checks for Choice fields and saves lots of time. If
178        a string/value is out of allowed range we get a value or key
179        error anyway.
180        """
181        return super(ChoiceConverter, self).toString(value=value,
182                                                     strict=strict)
183
184class DateConverter(Converter):
185    """A converter for zope.schema.IDate fields.
186
187    Converts date to string and vice versa. Stringified dates are
188    expected in format ``YYYY-MM-DD``.
189
190    To support at least some other formats, we accept for conversion
191    from string also the following formats:
192
193    * ``YYYY/MM/DD``
194   
195    * ``DD/MM/YYYY``
196   
197    * ``D/M/YYYY``
198
199    * ``DD.MM.YYYY``
200
201    * ``D.M.YYYY``
202
203    When converting to strings, always 'YYYY-MM-DD' is returned.
204
205    For convenience, when converting from strings also string data
206    separated by space is stripped before processing. Therefore
207    strings like '1990-04-01 12:12:01 GMT +1' will also work.
208    """
209    grok.context(IDate)
210    grok.provides(ISchemaTypeConverter)
211
212    #: List of supported date formats in `strftime()` notation.
213    formats = ['%Y-%m-%d', '%Y/%m/%d' , '%d/%m/%Y', '%D/%M/%Y',
214               '%d.%m.%Y', '%D.%M.%Y']
215   
216    def _convertValueFromString(self, string):
217        orig_string = string
218        if string is NONE_STRING_VALUE:
219            string = None
220        if string is None:
221            return None
222        value = None
223        if ' ' in string:
224            string = string.split(' ')[0]
225        for format in self.formats:
226            try:
227                value = datetime.datetime.strptime(string, format)
228                break
229            except ValueError:
230                pass
231        if value is None:
232            raise ValueError(
233                'Cannot convert to date: %s. Use YYYY-MM-DD.' %
234                orig_string)
235        value = value.date()
236        return value
237
238    def _convertValueToString(self, value):
239        return datetime.date.strftime(value, '%Y-%m-%d')
Note: See TracBrowser for help on using the repository browser.