Passed
Push — 2.x ( 130cd0...9a1936 )
by Ramon
06:07
created

DateTimeField.localize()   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 3
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2023 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
from AccessControl import ClassSecurityInfo
22
from App.class_init import InitializeClass
23
from Products.Archetypes.public import DateTimeField as BaseField
24
from Products.Archetypes.Registry import registerField
25
from Products.Archetypes.Registry import registerPropertyType
26
from senaite.core.api import dtime
27
from senaite.core.browser.widgets.datetimewidget import DateTimeWidget
28
from zope.i18n import translate
29
from zope.i18nmessageid import Message
30
31
from bika.lims import _
32
from bika.lims import api
33
34
WIDGET_NOPAST = "datepicker_nopast"
35
WIDGET_NOFUTURE = "datepicker_nofuture"
36
WIDGET_SHOWTIME = "show_time"
37
38
39
class DateTimeField(BaseField):
40
    """An improved DateTime Field. It allows to specify
41
    whether only dates or only times are interesting.
42
43
    This field is ported from Products.ATExtensions
44
    """
45
46
    _properties = BaseField._properties.copy()
47
    _properties.update({
48
        "type": "datetime_ng",
49
        "widget": DateTimeWidget,
50
        "with_time": 1,  # set to False if you want date only objects
51
        "with_date": 1,  # set to False if you want time only objects
52
        })
53
    security = ClassSecurityInfo()
54
55
    def validate(self, value, instance, errors=None, **kwargs):
56
        """Validate passed-in value using all field validators plus the
57
        validators for minimum and maximum date values
58
        Return None if all validations pass; otherwise, return the message of
59
        of the validation failure translated to current language
60
        """
61
        # Rely on the super-class first
62
        error = super(DateTimeField, self).validate(
63
            value, instance, errors=errors, **kwargs)
64
        if error:
65
            return error
66
67
        # Validate value is after min date
68
        error = self.validate_min_date(value, instance, errors=errors)
69
        if error:
70
            return error
71
72
        # Validate value is before max date
73
        error = self.validate_max_date(value, instance, errors=errors)
74
        if error:
75
            return error
76
77 View Code Duplication
    def validate_min_date(self, value, instance, errors=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
78
        """Validates the passed-in value against the field's minimum date
79
        """
80
        if errors is None:
81
            errors = {}
82
83
        # self.min always returns an offset-naive datetime, but the value
84
        # is offset-aware. We need to add the TZ, otherwise we get a:
85
        #   TypeError: can't compare offset-naive and offset-aware datetimes
86
        if dtime.to_ansi(value) >= dtime.to_ansi(self.min):
87
            return None
88
89
        error = _(
90
            u"error_datetime_before_min",
91
            default=u"${name} is before ${min_date}, please correct.",
92
            mapping={
93
                "name": self.get_label(instance),
94
                "min_date": self.localize(self.min, instance)
95
            }
96
        )
97
98
        field_name = self.getName()
99
        errors[field_name] = translate(error, context=api.get_request())
100
        return errors[field_name]
101
102 View Code Duplication
    def validate_max_date(self, value, instance, errors=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
103
        """Validates the passed-in value against the field's maximum date
104
        """
105
        if errors is None:
106
            errors = {}
107
108
        # self.max always returns an offset-naive datetime, but the value
109
        # is offset-aware. We need to add the TZ, otherwise we get a:
110
        #   TypeError: can't compare offset-naive and offset-aware datetimes
111
        if dtime.to_ansi(value) <= dtime.to_ansi(self.max):
112
            return None
113
114
        error = _(
115
            u"error_datetime_after_max",
116
            default=u"${name} is after ${max_date}, please correct.",
117
            mapping={
118
                "name": self.get_label(instance),
119
                "max_date": self.localize(self.max, instance)
120
            }
121
        )
122
123
        field_name = self.getName()
124
        errors[field_name] = translate(error, context=api.get_request())
125
        return errors[field_name]
126
127
    def is_true(self, val):
128
        """Returns whether val evaluates to True
129
        """
130
        val = str(val).strip().lower()
131
        return val in ["y", "yes", "1", "true", "on"]
132
133
    def get_label(self, instance):
134
        """Returns the translated label of this field for the given instance
135
        """
136
        request = api.get_request()
137
        label = self.widget.Label(instance)
138
        if isinstance(label, Message):
139
            return translate(label, context=request)
140
        return label
141
142
    def localize(self, dt, instance):
143
        """Returns the dt to localized time
144
        """
145
        request = api.get_request()
146
        return dtime.to_localized_time(dt, long_format=self.show_time,
147
                                       context=instance, request=request)
148
149
    @property
150
    def min(self):
151
        """Returns the minimum datetime supported by this field
152
        """
153
        no_past = getattr(self.widget, WIDGET_NOPAST, False)
154
        if self.is_true(no_past):
155
            return dtime.datetime.now()
156
        return dtime.datetime.min
157
158
    @property
159
    def max(self):
160
        """Returns the maximum datetime supported for this field
161
        """
162
        no_future = getattr(self.widget, WIDGET_NOFUTURE, False)
163
        if self.is_true(no_future):
164
            return dtime.datetime.now()
165
        return dtime.datetime.max
166
167
    @property
168
    def show_time(self):
169
        """Returns whether the time is displayed by the widget
170
        """
171
        show_time = getattr(self.widget, WIDGET_SHOWTIME, False)
172
        return self.is_true(show_time)
173
174
175
InitializeClass(DateTimeField)
176
177
178
registerField(
179
    DateTimeField,
180
    title="DateTime Field",
181
    description="An improved DateTimeField, which also allows time "
182
                "or date only specifications.")
183
184
185
registerPropertyType("with_time", "boolean", DateTimeField)
186
registerPropertyType("with_date", "boolean", DateTimeField)
187