Passed
Pull Request — 2.x (#1903)
by Ramon
05:54
created

DatetimeWidgetFactory()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nop 2
1
# -*- coding: utf-8 -*-
2
3
from datetime import datetime
4
5
import pytz
6
from bika.lims import api
7
from plone.app.z3cform.converters import DatetimeWidgetConverter
8
from Products.CMFPlone.utils import safe_callable
9
from senaite.core.interfaces import ISenaiteFormLayer
10
from senaite.core.schema.interfaces import IDatetimeField
11
from senaite.core.z3cform.interfaces import IDatetimeWidget
12
from z3c.form import interfaces
13
from z3c.form.browser import widget
14
from z3c.form.browser.widget import HTMLInputWidget
15
from z3c.form.interfaces import IFieldWidget
16
from z3c.form.widget import FieldWidget
17
from z3c.form.widget import Widget
18
from zope.component import adapter
19
from zope.interface import implementer
20
21
DATE_ONLY = ("{value.year:}-{value.month:02}-{value.day:02}")
22
23
DATE_AND_TIME = ("{value.year:}-{value.month:02}-{value.day:02} "
24
                 "{value.hour:02}:{value.minute:02}")
25
26
27
@adapter(IDatetimeField, interfaces.IWidget)
28
class DatetimeDataConverter(DatetimeWidgetConverter):
29
    """Converts the raw field data for widget/field usage
30
    """
31
    def toWidgetValue(self, value):
32
        """Converts from field value to widget.
33
34
        called by `z3c.form.widget.update`
35
36
        Bases:
37
            `plone.app.z3cform.converters.DatetimeWidgetConverter`
38
            `z3c.form.converter.DatetimeDataConverter`
39
40
        :param value: Field value.
41
        :type value: datetime
42
43
        :returns: Datetime in format `Y-m-d H:M`
44
        :rtype: string
45
        """
46
47
        if value is self.field.missing_value:
48
            return u""
49
        if getattr(self.widget, "show_time", None) is False:
50
            return DATE_ONLY.format(value=value)
51
        return DATE_AND_TIME.format(value=value)
52
53
    def toFieldValue(self, value):
54
        """Converts from widget value to field.
55
56
        :param value: Value inserted by datetime widget.
57
        :type value: string
58
59
        :returns: `datetime.datetime` object.
60
        :rtype: datetime
61
        """
62
        return super(DatetimeDataConverter, self).toFieldValue(value)
63
64
65
@implementer(IDatetimeWidget)
66
class DatetimeWidget(HTMLInputWidget, Widget):
67
    """Senaite date and time widget
68
    """
69
    klass = u"senaite-datetime-widget"
70
    # Olson DB/pytz timezone identifier or a callback
71
    # returning such an identifier.
72
    default_timezone = None
73
    # enable/disable time component
74
    show_time = True
75
    # disable past dates in the date picker
76
    datepicker_nopast = False
77
    # disable future dates in the date picker
78
    datepicker_nofuture = False
79
80
    def __init__(self, request, *args, **kw):
81
        super(DatetimeWidget, self).__init__(request)
82
        self.request = request
83
84
    def update(self):
85
        """Computes self.value for the widget templates
86
87
        see z3c.form.widget.Widget
88
        """
89
        super(DatetimeWidget, self).update()
90
        widget.addFieldClass(self)
91
92
    def to_local_date(self, value, length="short"):
93
        """Converts value to localized date
94
95
        Used in the display template to show a localized version of the date
96
97
        :param value: date or datetime
98
        :type value: string or datetime object
99
        :returns: localized date string
100
        """
101
        dt = self.to_datetime(value)
102
        if not dt:
103
            return ""
104
        df = "dateTime" if self.show_time else "date"
105
        formatter = self.request.locale.dates.getFormatter(df, length)
106
        return formatter.format(dt)
107
108
    def to_datetime(self, value):
109
        """convert date string to datetime object with tz
110
111
        NOTE: IMO opinion the timezone won't be ever set unless the widget
112
              `default_timezone` is set explicitly.
113
114
        code taken from here:
115
        `plone.app.z3cform.converters.DatetimeWidgetConverter`
116
117
        :param value: date or datetime
118
        :type value: string or datetime object
119
        :returns: datetime object
120
        """
121
        if isinstance(value, datetime):
122
            return value
123
        if not value:
124
            return ""
125
        tmp = value.split(" ")
126
        if not tmp[0]:
127
            return ""
128
        value = tmp[0].split("-")
129
        if len(tmp) == 2 and ":" in tmp[1]:
130
            value += tmp[1].split(":")
131
        else:
132
            value += ["00", "00"]
133
134
        default_zone = self.default_timezone
135
        zone = default_zone(self.context) \
136
            if safe_callable(default_zone) else default_zone
137
        ret = datetime(*map(int, value))
138
        if zone:
139
            tzinfo = pytz.timezone(zone)
140
            ret = tzinfo.localize(ret)
141
        return ret
142
143
    def get_date(self, value):
144
        """Return only the date part of the value
145
146
        :returns: date string
147
        """
148
        dt = self.to_datetime(value)
149
        if not dt:
150
            return ""
151
        return dt.strftime("%Y-%m-%d")
152
153
    def get_time(self, value):
154
        """Return only the time part of the value
155
156
        :returns: time string
157
        """
158
        dt = self.to_datetime(value)
159
        if not dt:
160
            return ""
161
        return dt.strftime("%H:%M")
162
163
    def get_max(self):
164
        """Return the max allowed date in the future
165
166
        :returns: date string
167
        """
168
        now = datetime.now()
169
        return now.strftime("%Y-%m-%d")
170
171
    def get_min(self):
172
        """Return the min allowed date in the past
173
174
        :returns: date string
175
        """
176
        now = datetime.now()
177
        return now.strftime("%Y-%m-%d")
178
179
    def attrs(self):
180
        """Return the template attributes for the date field
181
182
        :returns: dictionary of HTML attributes
183
        """
184
        attrs = {}
185
        if self.datepicker_nofuture:
186
            attrs["max"] = self.get_max()
187
        if self.datepicker_nopast:
188
            attrs["min"] = self.get_min()
189
        return attrs
190
191
    @property
192
    def portal(self):
193
        """Return the portal object
194
        """
195
        return api.get_portal()
196
197
    @property
198
    def portal_url(self):
199
        """Return the portal object URL
200
        """
201
        return api.get_url(self.portal)
202
203
204
@adapter(IDatetimeField, ISenaiteFormLayer)
205
@implementer(IFieldWidget)
206
def DatetimeWidgetFactory(field, request):
207
    """Widget factory for datetime field
208
    """
209
    return FieldWidget(field, DatetimeWidget(request))
210