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

Last change on this file since 8150 was 8150, checked in by uli, 12 years ago

Register FormattedDateDisplayWidget? for FormattedDate? schema type.

  • Property svn:keywords set to Id
File size: 7.6 KB
Line 
1## $Id: datewidget.py 8150 2012-04-13 21:44:00Z 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        self.date_format = getattr(context, 'date_format', self.date_format)
93        self.show_year = getattr(context, 'show_year', self.show_year)
94        # add css class determined by date_format and show_year
95        css_cls = FORMATS_BY_VALUE.get((self.date_format, self.show_year), '')
96        self.cssClass = ' '.join([self.cssClass, css_cls]).strip()
97        return super(FormattedDateWidget, self).__init__(
98            context, request, *args, **kw)
99
100    def _toFieldValue(self, input):
101        # In import files we can use the hash symbol at the end of a
102        # date string to avoid annoying automatic date transformation
103        # by Excel or Calc
104        input = input.strip('#')
105        if input == self._missing:
106            return self.context.missing_value
107        else:
108            try:
109                value = datetime.strptime(input, self.date_format)
110            except (ValueError, IndexError), v:
111                try:
112                    # Try ISO format as fallback.
113                    # This is needed for instance during imports.
114                    value = datetime.strptime(
115                        input, FormattedDateWidget.date_format)
116                except (ValueError, IndexError), v:
117                    raise ConversionError("Invalid datetime data", v)
118        return value.date()
119
120    def _toFormValue(self, value):
121        if value:
122            value = value.strftime(self.date_format)
123        return value
124
125
126class FormattedDateDisplayWidget(DateDisplayWidget):
127    """A date widget that supports different (and _explicit_) date formats.
128
129    This is a display widget.
130    """
131    date_format = '%Y-%m-%d'
132    show_year = False
133
134    implements(IDisplayWidget)
135
136    def __init__(self, context, request, *args, **kw):
137        # try to grab date_format and show_year from bound schema field.
138        self.date_format = getattr(context, 'date_format', self.date_format)
139        self.show_year = getattr(context, 'show_year', self.show_year)
140        return super(FormattedDateDisplayWidget, self).__init__(
141            context, request, *args, **kw)
142
143    def __call__(self):
144        if self._renderedValueSet():
145            content = self._data
146        else:
147            content = self.context.default
148        if content == self.context.missing_value:
149            return ""
150        content = content.strftime(self.date_format)
151        return renderElement("span", contents=escape(content),
152                             cssClass=self.cssClass)
153
154def FriendlyDateWidget(format):
155    """Get a friendly date input widget for `format`.
156
157    This widget is suitable for edit and add forms.
158
159    Valid `format` values are the keys of `DATE_FORMATS`
160    dict. Default is ``le`` (little endian; DD/MM/YYYY).
161
162    Friendly date widgets are rendered with a specialized CSS tag for
163    enabling JavaScript datepickers.
164    """
165    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
166    return CustomWidgetFactory(
167        FormattedDateWidget,
168        cssClass=css_class,
169        date_format=date_format)
170
171def FriendlyDateDisplayWidget(format):
172    """Get a friendly date display widget for `format`.
173
174    This widget is suitable for display forms.
175
176    Valid `format` values are the keys of `DATE_FORMATS`
177    dict. Default is ``le`` (little endian; DD/MM/YYYY).
178
179    This widget is not rendered with a specialized CSS tag for
180    enabling JavaScript datepickers. `css_class` is ignored which means
181    there is nor difference between e.g. ``le`` and ``le-year``.`
182    """
183    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
184    return CustomWidgetFactory(
185        FormattedDateDisplayWidget,
186        date_format=date_format)
187
188def FriendlyDatetimeDisplayWidget(format):
189    """Get a friendly datetime display widget for `format`.
190
191    This widget is suitable for display forms.
192
193    Valid `format` values are the keys of `DATE_FORMATS`
194    dict. Default is ``le`` (little endian; DD/MM/YYYY %H:%M:%S).
195
196    This widget is not rendered with a specialized CSS tag for
197    enabling JavaScript datepickers. `css_class` is ignored which means
198    there is no difference between e.g. ``le`` and ``le-year``.`
199    """
200    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
201    datetime_format = date_format + ' %H:%M:%S'
202    return CustomWidgetFactory(
203        FormattedDateDisplayWidget,
204        date_format=datetime_format)
Note: See TracBrowser for help on using the repository browser.