source: main/waeup.kofa/trunk/src/waeup/kofa/widgets/datewidget.py @ 8171

Last change on this file since 8171 was 8152, checked in by uli, 13 years ago
  • Add new default date widgets for different date formats.
  • In FormattedDateWidgets? use own default also, if no date format was set with the connected schema field. In former implementation we expected always a valid date_format from connected schema field or no date_format at all. But as FormattedDate? schema fields now may also return None as date_format, which means that the date_format was not set at all in the interface, we can override this value with respective default values.

As a result, when setting no date_format in an interface with
FormattedDate? schema fields and when rendered with one of the
FormattedDateWidgets?, the default format of the respective widget
will be used to render the value: %d.%m.%Y for german dates, etc.

  • Property svn:keywords set to Id
File size: 8.2 KB
Line 
1## $Id: datewidget.py 8152 2012-04-14 14:13:39Z uli $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19A datewidget with customizable date format.
20"""
21from datetime import datetime
22from zope.formlib.interfaces import ConversionError, IDisplayWidget
23from zope.formlib.textwidgets import DateWidget, DateDisplayWidget, escape
24from zope.formlib.widget import renderElement, CustomWidgetFactory
25from zope.interface import implements
26
27#: A dictionary of supported date formats.
28#:
29#: The following formats are supported:
30#:
31#: ``iso``
32#:    ISO format ``YYYY-MM-DD``
33#: ``le``
34#:    little endian with slashes: ``DD/MM/YYYY``
35#: ``de``
36#:    german date format: ``DD.MM.YYYY``
37#: ``us``
38#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
39#:
40#: Furthermore we support for input widgets an additional year
41#: marker. Input date widgets with this marker provide also a year
42#: selector, handy for dates of birth etc.
43#:
44#: The year-supporting formats are similar to the basic versions above:
45#:
46#: ``iso-year``
47#:    ISO format ``YYYY-MM-DD``
48#: ``le-year``
49#:    little endian with slashes: ``DD/MM/YYYY``
50#: ``de-year``
51#:    german date format: ``DD.MM.YYYY``
52#: ``us-year``
53#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
54#:
55#: For date display widgets there is naturally no difference between a
56#: year and non-year setting (you can for instance use 'le' or 'le-year'
57#: with the same output).
58DATE_FORMATS = {
59    'iso': ('datepicker', '%Y-%m-%d'),
60    'le':  ('datepicker-le', '%d/%m/%Y'),
61    'de':  ('datepicker-de', '%d.%m.%Y'),
62    'us':  ('datepicker-us', '%m/%d/%Y'),
63    'iso-year': ('datepicker-year', '%Y-%m-%d'),
64    'le-year':  ('datepicker-le-year', '%d/%m/%Y'),
65    'de-year':  ('datepicker-de-year', '%d.%m.%Y'),
66    'us-year':  ('datepicker-us-year', '%m/%d/%Y'),
67    }
68
69#: a dict containing tuples (<FORMAT>, <SHOW_YEAR>) as keys and
70#: suitable CSS tags as values.
71FORMATS_BY_VALUE = dict(
72    [((val[1], 'year' in key), val[0]) for key, val in DATE_FORMATS.items()])
73
74class FormattedDateWidget(DateWidget):
75    """A date widget that supports different (and _explicit_) date formats.
76
77    If the widget is bound to a schema field with respective
78    attributes, it reads its `show_year` and `date_format` attributes
79    (see waeup.kofa.schema.FormattedDate for an example) and sets a
80    CSS tag according to these values.
81
82    The widget also accepts ISO format as a fallback, even if a
83    different format was set. This should help with imports.
84
85    This is an input widget.
86    """
87    date_format = '%Y-%m-%d'
88    show_year = False
89
90    def __init__(self, context, request, *args, **kw):
91        # try to grab date_format and show_year from bound schema field.
92        date_format = getattr(context, 'date_format', self.date_format)
93        if date_format is not None:
94            self.date_format = date_format
95        self.show_year = getattr(context, 'show_year', self.show_year)
96        # add css class determined by date_format and show_year
97        css_cls = FORMATS_BY_VALUE.get((self.date_format, self.show_year), '')
98        self.cssClass = ' '.join([self.cssClass, css_cls]).strip()
99        return super(FormattedDateWidget, self).__init__(
100            context, request, *args, **kw)
101
102    def _toFieldValue(self, input):
103        # In import files we can use the hash symbol at the end of a
104        # date string to avoid annoying automatic date transformation
105        # by Excel or Calc
106        input = input.strip('#')
107        if input == self._missing:
108            return self.context.missing_value
109        else:
110            try:
111                value = datetime.strptime(input, self.date_format)
112            except (ValueError, IndexError), v:
113                try:
114                    # Try ISO format as fallback.
115                    # This is needed for instance during imports.
116                    value = datetime.strptime(
117                        input, FormattedDateWidget.date_format)
118                except (ValueError, IndexError), v:
119                    raise ConversionError("Invalid datetime data", v)
120        return value.date()
121
122    def _toFormValue(self, value):
123        if value:
124            value = value.strftime(self.date_format)
125        return value
126
127
128class FormattedDateDisplayWidget(DateDisplayWidget):
129    """A date widget that supports different (and _explicit_) date formats.
130
131    This is a display widget.
132    """
133    date_format = '%Y-%m-%d'
134    show_year = False
135
136    implements(IDisplayWidget)
137
138    def __init__(self, context, request, *args, **kw):
139        # try to grab date_format and show_year from bound schema field.
140        date_format = getattr(context, 'date_format', self.date_format)
141        if date_format is not None:
142            self.date_format = date_format
143        self.show_year = getattr(context, 'show_year', self.show_year)
144        return super(FormattedDateDisplayWidget, self).__init__(
145            context, request, *args, **kw)
146
147    def __call__(self):
148        if self._renderedValueSet():
149            content = self._data
150        else:
151            content = self.context.default
152        if content == self.context.missing_value:
153            return ""
154        content = content.strftime(self.date_format)
155        return renderElement("span", contents=escape(content),
156                             cssClass=self.cssClass)
157
158class DateLEWidget(FormattedDateWidget):
159    date_format = '%d/%m/%Y'
160
161class DateDEWidget(FormattedDateWidget):
162    date_format = '%d.%m.%Y'
163
164class DateUSWidget(FormattedDateWidget):
165    date_format = '%m/%d/%Y'
166
167class DateLEDisplayWidget(FormattedDateDisplayWidget):
168    date_format = '%d/%m/%Y'
169
170class DateDEDisplayWidget(FormattedDateDisplayWidget):
171    date_format = '%d.%m.%Y'
172
173class DateUSDisplayWidget(FormattedDateDisplayWidget):
174    date_format = '%m/%d/%Y'
175
176def FriendlyDateWidget(format):
177    """Get a friendly date input widget for `format`.
178
179    This widget is suitable for edit and add forms.
180
181    Valid `format` values are the keys of `DATE_FORMATS`
182    dict. Default is ``le`` (little endian; DD/MM/YYYY).
183
184    Friendly date widgets are rendered with a specialized CSS tag for
185    enabling JavaScript datepickers.
186    """
187    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
188    return CustomWidgetFactory(
189        FormattedDateWidget,
190        cssClass=css_class,
191        date_format=date_format)
192
193def FriendlyDateDisplayWidget(format):
194    """Get a friendly date display widget for `format`.
195
196    This widget is suitable for display forms.
197
198    Valid `format` values are the keys of `DATE_FORMATS`
199    dict. Default is ``le`` (little endian; DD/MM/YYYY).
200
201    This widget is not rendered with a specialized CSS tag for
202    enabling JavaScript datepickers. `css_class` is ignored which means
203    there is nor difference between e.g. ``le`` and ``le-year``.`
204    """
205    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
206    return CustomWidgetFactory(
207        FormattedDateDisplayWidget,
208        date_format=date_format)
209
210def FriendlyDatetimeDisplayWidget(format):
211    """Get a friendly datetime display widget for `format`.
212
213    This widget is suitable for display forms.
214
215    Valid `format` values are the keys of `DATE_FORMATS`
216    dict. Default is ``le`` (little endian; DD/MM/YYYY %H:%M:%S).
217
218    This widget is not rendered with a specialized CSS tag for
219    enabling JavaScript datepickers. `css_class` is ignored which means
220    there is no difference between e.g. ``le`` and ``le-year``.`
221    """
222    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
223    datetime_format = date_format + ' %H:%M:%S'
224    return CustomWidgetFactory(
225        FormattedDateDisplayWidget,
226        date_format=datetime_format)
Note: See TracBrowser for help on using the repository browser.