source: main/waeup.sirp/trunk/src/waeup/sirp/utils/converters.py @ 6260

Last change on this file since 6260 was 6260, checked in by uli, 13 years ago

Add own getWidgetsDate function to retrieve bindings of errors to form
fields.

File size: 9.9 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')
240
241
242from zope.interface import Interface
243from zope.formlib import form
244from zope.formlib.form import (
245    _widgetKey, WidgetInputError, ValidationError, InputErrors, expandPrefix)
246from zope.formlib.interfaces import IInputWidget
247from zope.publisher.browser import TestRequest
248
249def getWidgetsData(widgets, form_prefix, data):
250    # from zope.formlib.form
251    errors = []
252    form_prefix = expandPrefix(form_prefix)
253
254    for input, widget in widgets.__iter_input_and_widget__():
255        if input and IInputWidget.providedBy(widget):
256            name = _widgetKey(widget, form_prefix)
257
258            if not widget.hasInput():
259                continue
260
261            try:
262                data[name] = widget.getInputValue()
263            except ValidationError, error:
264                # convert field ValidationError to WidgetInputError
265                error = WidgetInputError(widget.name, widget.label, error)
266                errors.append((name, error))
267            except InputErrors, error:
268                errors.append((name, error))
269
270    return errors
271
272
273class DefaultConverter(Converter):
274
275    grok.context(Interface)
276    grok.provides(ISchemaTypeConverter)
277
278    def __init__(self, iface, form_fields=None):
279        self.iface = iface
280        self.form_fields = form_fields
281        if self.form_fields is None:
282            self.form_fields = form.Fields(iface)
283        return
284
285    def applyRowData(self, data_dict, context):
286        request = TestRequest(form={})
287        #form_fields = form.Fields(self.iface)
288        form_fields = self.form_fields
289        for key, val in data_dict.items():
290            request.form['form.%s' % key] = val
291        widgets = form.setUpWidgets(
292            form_fields, 'form', context, request)
293        #errors = form.getWidgetsData(widgets, 'form', data_dict)
294        errors = getWidgetsData(widgets, 'form', data_dict)
295        if errors:
296            err_message = []
297            for key, error in errors:
298                message = error[1].args[0]
299                err_message.append((key, message))
300        invariant_errors = form.checkInvariants(form_fields, data_dict)
301        if not errors and not invariant_errors:
302            changed = form.applyChanges(
303                context, form_fields, data_dict)
304        return errors, invariant_errors, data_dict
Note: See TracBrowser for help on using the repository browser.