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

Last change on this file since 16302 was 9498, checked in by Henrik Bettermann, 12 years ago

Emergency fix so that student who entered a wrong year can view and edit their form.

  • Property svn:keywords set to Id
File size: 8.9 KB
RevLine 
[7196]1## $Id: datewidget.py 9498 2012-11-01 15:41:38Z henrik $
[6050]2##
[6867]3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
[6050]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.
[7196]8##
[6050]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.
[7196]13##
[6050]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"""
[8193]21import pytz
[6050]22from datetime import datetime
[8193]23from zope.component import queryUtility
[6056]24from zope.formlib.interfaces import ConversionError, IDisplayWidget
[8147]25from zope.formlib.textwidgets import DateWidget, DateDisplayWidget, escape
[6056]26from zope.formlib.widget import renderElement, CustomWidgetFactory
27from zope.interface import implements
[8193]28from waeup.kofa.interfaces import IKofaUtils
29from waeup.kofa.utils.helpers import to_timezone
[6050]30
[8193]31
[6056]32#: A dictionary of supported date formats.
[6057]33#:
34#: The following formats are supported:
35#:
36#: ``iso``
37#:    ISO format ``YYYY-MM-DD``
38#: ``le``
39#:    little endian with slashes: ``DD/MM/YYYY``
40#: ``de``
41#:    german date format: ``DD.MM.YYYY``
42#: ``us``
43#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
44#:
45#: Furthermore we support for input widgets an additional year
46#: marker. Input date widgets with this marker provide also a year
47#: selector, handy for dates of birth etc.
48#:
49#: The year-supporting formats are similar to the basic versions above:
50#:
51#: ``iso-year``
52#:    ISO format ``YYYY-MM-DD``
53#: ``le-year``
54#:    little endian with slashes: ``DD/MM/YYYY``
55#: ``de-year``
56#:    german date format: ``DD.MM.YYYY``
57#: ``us-year``
58#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
[8147]59#:
[6057]60#: For date display widgets there is naturally no difference between a
61#: year and non-year setting (you can for instance use 'le' or 'le-year'
62#: with the same output).
[6056]63DATE_FORMATS = {
[6060]64    'iso': ('datepicker', '%Y-%m-%d'),
65    'le':  ('datepicker-le', '%d/%m/%Y'),
66    'de':  ('datepicker-de', '%d.%m.%Y'),
67    'us':  ('datepicker-us', '%m/%d/%Y'),
68    'iso-year': ('datepicker-year', '%Y-%m-%d'),
69    'le-year':  ('datepicker-le-year', '%d/%m/%Y'),
70    'de-year':  ('datepicker-de-year', '%d.%m.%Y'),
71    'us-year':  ('datepicker-us-year', '%m/%d/%Y'),
[6056]72    }
[8147]73
74#: a dict containing tuples (<FORMAT>, <SHOW_YEAR>) as keys and
75#: suitable CSS tags as values.
76FORMATS_BY_VALUE = dict(
77    [((val[1], 'year' in key), val[0]) for key, val in DATE_FORMATS.items()])
78
79class FormattedDateWidget(DateWidget):
80    """A date widget that supports different (and _explicit_) date formats.
81
82    If the widget is bound to a schema field with respective
83    attributes, it reads its `show_year` and `date_format` attributes
84    (see waeup.kofa.schema.FormattedDate for an example) and sets a
85    CSS tag according to these values.
86
87    The widget also accepts ISO format as a fallback, even if a
88    different format was set. This should help with imports.
89
90    This is an input widget.
91    """
92    date_format = '%Y-%m-%d'
93    show_year = False
94
95    def __init__(self, context, request, *args, **kw):
96        # try to grab date_format and show_year from bound schema field.
[8152]97        date_format = getattr(context, 'date_format', self.date_format)
98        if date_format is not None:
99            self.date_format = date_format
[8147]100        self.show_year = getattr(context, 'show_year', self.show_year)
[8150]101        # add css class determined by date_format and show_year
102        css_cls = FORMATS_BY_VALUE.get((self.date_format, self.show_year), '')
103        self.cssClass = ' '.join([self.cssClass, css_cls]).strip()
[8147]104        return super(FormattedDateWidget, self).__init__(
105            context, request, *args, **kw)
106
107    def _toFieldValue(self, input):
108        # In import files we can use the hash symbol at the end of a
109        # date string to avoid annoying automatic date transformation
110        # by Excel or Calc
111        input = input.strip('#')
112        if input == self._missing:
113            return self.context.missing_value
114        else:
115            try:
116                value = datetime.strptime(input, self.date_format)
117            except (ValueError, IndexError), v:
118                try:
119                    # Try ISO format as fallback.
120                    # This is needed for instance during imports.
121                    value = datetime.strptime(
122                        input, FormattedDateWidget.date_format)
123                except (ValueError, IndexError), v:
124                    raise ConversionError("Invalid datetime data", v)
125        return value.date()
126
127    def _toFormValue(self, value):
128        if value:
[9498]129            try:
130                value = value.strftime(self.date_format)
131            except ValueError:
132                return value
[8147]133        return value
134
[8150]135
[8147]136class FormattedDateDisplayWidget(DateDisplayWidget):
137    """A date widget that supports different (and _explicit_) date formats.
138
139    This is a display widget.
[8193]140
141    It can also be used for displaying datetimes. If used to display a
142    datetime (not a date), the widget returns local datetime with
143    timezone set according to KofaUtils.
[8147]144    """
145    date_format = '%Y-%m-%d'
[8150]146    show_year = False
147
[8147]148    implements(IDisplayWidget)
149
[8150]150    def __init__(self, context, request, *args, **kw):
151        # try to grab date_format and show_year from bound schema field.
[8152]152        date_format = getattr(context, 'date_format', self.date_format)
153        if date_format is not None:
154            self.date_format = date_format
[8150]155        self.show_year = getattr(context, 'show_year', self.show_year)
156        return super(FormattedDateDisplayWidget, self).__init__(
157            context, request, *args, **kw)
158
[8147]159    def __call__(self):
160        if self._renderedValueSet():
161            content = self._data
162        else:
163            content = self.context.default
164        if content == self.context.missing_value:
165            return ""
[8193]166        if isinstance(content, datetime):
167            # shift value to local timezone
168            tz = pytz.utc
169            utils = queryUtility(IKofaUtils)
170            if utils is not None:
171                tz = utils.tzinfo
172            content = to_timezone(content, tz)
[9498]173        try:
174            content = content.strftime(self.date_format)
175        except ValueError:
176            return None
[8147]177        return renderElement("span", contents=escape(content),
178                             cssClass=self.cssClass)
179
[8152]180class DateLEWidget(FormattedDateWidget):
181    date_format = '%d/%m/%Y'
182
183class DateDEWidget(FormattedDateWidget):
184    date_format = '%d.%m.%Y'
185
186class DateUSWidget(FormattedDateWidget):
187    date_format = '%m/%d/%Y'
188
189class DateLEDisplayWidget(FormattedDateDisplayWidget):
190    date_format = '%d/%m/%Y'
191
192class DateDEDisplayWidget(FormattedDateDisplayWidget):
193    date_format = '%d.%m.%Y'
194
195class DateUSDisplayWidget(FormattedDateDisplayWidget):
196    date_format = '%m/%d/%Y'
197
[6056]198def FriendlyDateWidget(format):
199    """Get a friendly date input widget for `format`.
[6057]200
201    This widget is suitable for edit and add forms.
[8147]202
[7599]203    Valid `format` values are the keys of `DATE_FORMATS`
[6057]204    dict. Default is ``le`` (little endian; DD/MM/YYYY).
205
206    Friendly date widgets are rendered with a specialized CSS tag for
207    enabling JavaScript datepickers.
[6056]208    """
209    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
210    return CustomWidgetFactory(
211        FormattedDateWidget,
212        cssClass=css_class,
213        date_format=date_format)
214
215def FriendlyDateDisplayWidget(format):
216    """Get a friendly date display widget for `format`.
[6057]217
218    This widget is suitable for display forms.
[8147]219
[7599]220    Valid `format` values are the keys of `DATE_FORMATS`
[6057]221    dict. Default is ``le`` (little endian; DD/MM/YYYY).
222
[6060]223    This widget is not rendered with a specialized CSS tag for
[8147]224    enabling JavaScript datepickers. `css_class` is ignored which means
[6060]225    there is nor difference between e.g. ``le`` and ``le-year``.`
[6056]226    """
227    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
228    return CustomWidgetFactory(
229        FormattedDateDisplayWidget,
230        date_format=date_format)
[6867]231
232def FriendlyDatetimeDisplayWidget(format):
233    """Get a friendly datetime display widget for `format`.
234
235    This widget is suitable for display forms.
236
[7599]237    Valid `format` values are the keys of `DATE_FORMATS`
[6868]238    dict. Default is ``le`` (little endian; DD/MM/YYYY %H:%M:%S).
[6867]239
240    This widget is not rendered with a specialized CSS tag for
241    enabling JavaScript datepickers. `css_class` is ignored which means
[8147]242    there is no difference between e.g. ``le`` and ``le-year``.`
[6867]243    """
244    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
245    datetime_format = date_format + ' %H:%M:%S'
246    return CustomWidgetFactory(
[6868]247        FormattedDateDisplayWidget,
248        date_format=datetime_format)
Note: See TracBrowser for help on using the repository browser.