Passed
Push — 2.x ( 8942fe...e5fb61 )
by Jordi
05:53
created

SecondaryDateSampledFieldVisibility.isVisible()   A

Complexity

Conditions 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 13
rs 10
c 0
b 0
f 0
cc 3
nop 4
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-2021 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
from bika.lims import api
22
from bika.lims.interfaces import IATWidgetVisibility
23
from bika.lims.interfaces import IAnalysisRequestSecondary
24
from bika.lims.interfaces import IBatch
25
from bika.lims.interfaces import IClient
26
from bika.lims.utils import getHiddenAttributesForClass
27
from zope.interface import implements
28
29
_marker = []
30
31
32
class SenaiteATWidgetVisibility(object):
33
    implements(IATWidgetVisibility)
34
35
    def __init__(self, context, sort=1000, field_names=None):
36
        self.context = context
37
        self.sort = sort
38
        self.field_names = field_names or list()
39
40
    def __call__(self, context, mode, field, default):
41
        state = default if default else "visible"
42
        if not field or field.getName() not in self.field_names:
43
            return state
44
        return self.isVisible(field, mode, default)
45
46
    def isVisible(self, field, mode="view", default="visible"):
47
        """Returns if the field is visible in a given mode
48
49
        Possible returned values are:
50
        - hidden: Field rendered as a hidden input field
51
        - invisible: Field not rendered at all
52
        - visible: Field rendered as a label or editable field depending on the
53
            mode. E.g. if mode is "edit" and the value returned is "visible",
54
            the field will be rendered as an input. If the mode is "view", the
55
            field will be rendered as a span.
56
        """
57
        raise NotImplementedError("Must be implemented by subclass")
58
59
60
class ClientFieldVisibility(SenaiteATWidgetVisibility):
61
    """The Client field is editable by default in ar_add.  This adapter
62
    will force the Client field to be hidden when it should not be set
63
    by the user.
64
    """
65
    def __init__(self, context):
66
        super(ClientFieldVisibility, self).__init__(
67
            context=context, sort=10, field_names=["Client"])
68
69
    def isVisible(self, field, mode="view", default="visible"):
70
        if mode == "add":
71
            parent = self.context.aq_parent
72
            if IClient.providedBy(parent):
73
                # Note we return "hidden" here instead of "invisible": we want
74
                # the field to be auto-filled and processed on submit
75
                return "hidden"
76
            if IBatch.providedBy(parent) and parent.getClient():
77
                # The Batch has a Client assigned already!
78
                # Note we can have Batches without a client assigned
79
                return "hidden"
80
        elif mode == "edit":
81
            # This is already managed by wf permission, but is **never** a good
82
            # idea to allow the user to change the Client from an AR (basically
83
            # because otherwise, we'd need to move the object from one client
84
            # folder to another!).
85
            return "invisible"
86
        return default
87
88
89
class BatchFieldVisibility(SenaiteATWidgetVisibility):
90
    """This will force the 'Batch' field to 'hidden' in ar_add when the parent
91
    context is a Batch and in Analysis Request view when current user is a
92
    client and the assigned batch does not have a client assigned.
93
    """
94
    def __init__(self, context):
95
        super(BatchFieldVisibility, self).__init__(
96
            context=context, sort=10, field_names=["Batch"])
97
98
    def isVisible(self, field, mode="view", default="visible"):
99
        if IBatch.providedBy(self.context.aq_parent):
100
            return "hidden"
101
102
        if mode == "edit":
103
            client = api.get_current_client()
104
            if client:
105
                # If current user is a client contact and the batch this Sample
106
                # is assigned to does not have a client assigned (e.g., the
107
                # batch was assigned by lab personnel), hide this field
108
                batch = self.context.getBatch()
109
                if batch and batch.getClient() != client:
110
                    return "invisible"
111
112
        return default
113
114
115
class PreservationFieldsVisibility(SenaiteATWidgetVisibility):
116
    """Display/Hide fields related with Preservation Workflow
117
    """
118
    def __init__(self, context):
119
        super(PreservationFieldsVisibility, self).__init__(
120
            context=context, sort=10,
121
            field_names=["DatePreserved", "Preserver"])
122
123
    def isVisible(self, field, mode="view", default="visible"):
124
        if not self.context.bika_setup.getSamplePreservationEnabled():
125
            return "invisible"
126
        return default
127
128
129
class ScheduledSamplingFieldsVisibility(SenaiteATWidgetVisibility):
130
    """Display/Hide fields related with ScheduledSampling Workflow
131
    """
132
    def __init__(self, context):
133
        super(ScheduledSamplingFieldsVisibility, self).__init__(
134
            context=context, sort=10,
135
            field_names=["ScheduledSamplingSampler"])
136
137
    def isVisible(self, field, mode="view", default="visible"):
138
        if not self.context.bika_setup.getScheduleSamplingEnabled():
139
            return "invisible"
140
        return default
141
142
143
class SamplingFieldsVisibility(SenaiteATWidgetVisibility):
144
    """
145
    This will handle Handling 'DateSampled' and 'SamplingDate' fields'
146
    visibilities based on Sampling Workflow (SWF)status. We must check the
147
    attribute saved on the sample, not the bika_setup value though. See the
148
    internal comments how it enables/disables WidgetVisibility depending on SWF.
149
    """
150
    def __init__(self, context):
151
        super(SamplingFieldsVisibility, self).__init__(
152
            context=context, sort=10,
153
            field_names=["Sampler", "DateSampled", "SamplingDate"])
154
155
    def isVisible(self, field, mode="view", default="visible"):
156
        # If object has been already created, get SWF statues from it.
157
        swf_enabled = False
158
        if hasattr(self.context, 'getSamplingWorkflowEnabled') and \
159
                self.context.getSamplingWorkflowEnabled() != '':
160
            swf_enabled = self.context.getSamplingWorkflowEnabled()
161
        else:
162
            swf_enabled = self.context.bika_setup.getSamplingWorkflowEnabled()
163
164
        if mode == "add":
165
            if field.getName() == "DateSampled":
166
                field.required = not swf_enabled
167
                return swf_enabled and "invisible" or "edit"
168
            elif field.getName() == "SamplingDate":
169
                field.required = swf_enabled
170
                return swf_enabled and "edit" or "invisible"
171
            elif field.getName() == "Sampler":
172
                return swf_enabled and "edit" or "invisible"
173
174
        elif not swf_enabled:
175
            if field.getName() != "DateSampled":
176
                return "invisible"
177
178
        return default
179
180
181
class RegistryHiddenFieldsVisibility(SenaiteATWidgetVisibility):
182
    """Do not display fields declared in bika.lims.hiddenattributes registry key
183
    """
184
    def __init__(self, context):
185
        field_names = getHiddenAttributesForClass(context.portal_type)
186
        super(RegistryHiddenFieldsVisibility, self).__init__(
187
            context=context, sort=float("inf"), field_names=[field_names, ])
188
189
    def isVisible(self, field, mode="view", default="visible"):
190
        return "invisible"
191
192
193
class DateReceivedFieldVisibility(SenaiteATWidgetVisibility):
194
    """DateReceived is editable in sample context, only if all related analyses
195
    are not yet submitted and if not a secondary sample.
196
    """
197
    def __init__(self, context):
198
        super(DateReceivedFieldVisibility, self).__init__(
199
            context=context, sort=3, field_names=["DateReceived"])
200
201
    def isVisible(self, field, mode="view", default="visible"):
202
        """Returns whether the field is visible in a given mode
203
        """
204
        if mode != "edit":
205
            return default
206
207
        # If this is a Secondary Analysis Request, this field is not editable
208
        if IAnalysisRequestSecondary.providedBy(self.context):
209
            return "invisible"
210
211
        return self.context.isOpen() and "visible" or "invisible"
212
213
214
class SecondaryDateSampledFieldVisibility(SenaiteATWidgetVisibility):
215
    """DateSampled is editable in sample unless secondary sample
216
    """
217
    def __init__(self, context):
218
        super(SecondaryDateSampledFieldVisibility, self).__init__(
219
            context=context, sort=20, field_names=["DateSampled"])
220
221
    def isVisible(self, field, mode="view", default="visible"):
222
        """Returns whether the field is visible in a given mode
223
        """
224
        if mode != "edit":
225
            return default
226
227
        # If this is a Secondary Analysis Request, this field is not editable
228
        if IAnalysisRequestSecondary.providedBy(self.context):
229
            return "invisible"
230
231
        # Delegate to SamplingFieldsVisibility adapter
232
        return SamplingFieldsVisibility(self.context).isVisible(
233
            field, mode=mode, default=default)
234
235
236
class PrimaryAnalysisRequestFieldVisibility(SenaiteATWidgetVisibility):
237
    """PrimarySample field is not visible unless the current Sample is a
238
    Secondary Sample. And even in such case, the field cannot be edited
239
    """
240
    def __init__(self, context):
241
        super(PrimaryAnalysisRequestFieldVisibility, self).__init__(
242
            context=context, sort=3, field_names=["PrimaryAnalysisRequest"])
243
244
    def isVisible(self, field, mode="view", default="visible"):
245
        """Returns whether the field is visible in a given mode
246
        """
247
        if mode == "add":
248
            return default
249
250
        if not IAnalysisRequestSecondary.providedBy(self.context):
251
            # If not a secondary Analysis Request, don't render the field
252
            return "hidden"
253
254
        # No mather if the mode is edit or view, display it always as readonly
255
        if mode == "edit":
256
            return "invisible"
257
258
        return default
259
260
261
class BatchClientFieldVisibility(SenaiteATWidgetVisibility):
262
    """Client field in a Batch in only editable while it is being created or
263
    when the Batch does not contain any sample
264
    """
265
    def __init__(self, context):
266
        super(BatchClientFieldVisibility, self).__init__(
267
            context=context, sort=3, field_names=["Client"])
268
269
    def isVisible(self, field, mode="view", default="visible"):
270
        """Returns whether the field is visible in a given state
271
        """
272
        if self.context.getClient():
273
            # This batch has a client assigned already and this cannot be
274
            # changed to prevent inconsistencies (client contacts can access
275
            # to batches that belong to their same client)
276
            return "invisible"
277
278
        if mode == "edit":
279
            # This batch does not have a client assigned, but allow the client
280
            # field to be editable only if does not contain any sample
281
            if self.context.getAnalysisRequestsBrains():
282
                return "invisible"
283
284
        return default
285
286
287
class InternalUseFieldVisibility(SenaiteATWidgetVisibility):
288
    """InternalUse field must only be visible to lab personnel
289
    """
290
    def __init__(self, context):
291
        super(InternalUseFieldVisibility, self).__init__(
292
            context=context, sort=3, field_names=["InternalUse"])
293
294
    def isVisible(self, field, mode="view", default="visible"):
295
        """Returns whether the field is visible in a given mode
296
        """
297
        return api.get_current_client() and "invisible" or default
298