Passed
Push — master ( 84f414...5f3531 )
by Jordi
09:26 queued 03:20
created

bika.lims.adapters.widgetvisibility   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 259
Duplicated Lines 13.13 %

Importance

Changes 0
Metric Value
wmc 61
eloc 156
dl 34
loc 259
rs 3.52
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A OptionalFieldsWidgetVisibility.__call__() 0 6 3
F SamplingWorkflowWidgetVisibility.__call__() 0 50 17
A HideClientDiscountFields.__init__() 3 3 1
A HideClientDiscountFields.__call__() 8 8 4
A BatchARAdd_BatchFieldWidgetVisibility.__init__() 0 3 1
A OptionalFieldsWidgetVisibility.__init__() 0 3 1
A WorkflowAwareWidgetVisibility.__init__() 0 3 1
A HideARPriceFields.__init__() 3 3 1
A SamplingWorkflowWidgetVisibility.__init__() 0 3 1
A BatchARAdd_BatchFieldWidgetVisibility.__call__() 0 6 4
A HideARPriceFields.__call__() 8 8 4
C WorkflowAwareWidgetVisibility.__call__() 0 24 9
A ClientFieldWidgetVisibility.__init__() 0 3 1
B ClientFieldWidgetVisibility.__call__() 0 15 6
A SampleDateReceived.__init__() 0 3 1
B SampleDateReceived.__call__() 0 14 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like bika.lims.adapters.widgetvisibility often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE
4
#
5
# Copyright 2018 by it's authors.
6
# Some rights reserved. See LICENSE.rst, CONTRIBUTORS.rst.
7
from bika.lims.api import get_tool
8
from bika.lims.interfaces import IAnalysisRequestsFolder, IBatch, IClient
9
from bika.lims.interfaces import IATWidgetVisibility
10
from bika.lims.utils import getHiddenAttributesForClass
11
from Products.CMFCore.utils import getToolByName
12
from Products.CMFCore.WorkflowCore import WorkflowException
13
from types import DictType
14
from zope.interface import implements
15
16
_marker = []
17
18
19
class WorkflowAwareWidgetVisibility(object):
20
    """This adapter allows the schema definition to have different widget visibility
21
    settings for different workflow states in the primary review_state workflow.
22
23
    With this it is possible to write:
24
25
        StringField(
26
            'fieldName',
27
            widget=StringWidget(
28
                label=_('field Name'),
29
                visible = {
30
                    'edit': 'visible',  # regular AT uses these and they override
31
                    'view': 'visible',  # everything, without 'edit' you cannot edit
32
                    'wf_state':    {'edit': 'invisible', 'view': 'visible'  },
33
                    'other_state': {'edit': 'visible',   'view': 'invisible'},
34
            }
35
36
    The rules about defaults, "hidden", "visible" and "invisible" are the same
37
    as those from the default Products.Archetypes.Widget.TypesWidget#isVisible
38
39
    """
40
    implements(IATWidgetVisibility)
41
42
    def __init__(self, context):
43
        self.context = context
44
        self.sort = 100
45
46
    def __call__(self, context, mode, field, default):
47
        """
48
        """
49
        state = default if default else 'visible'
50
        workflow = getToolByName(self.context, 'portal_workflow')
51
        try:
52
            review_state = workflow.getInfoFor(self.context, 'review_state')
53
        except WorkflowException:
54
            return state
55
        vis_dic = field.widget.visible
56
        if type(vis_dic) is not DictType or review_state not in vis_dic:
57
            return state
58
        inner_vis_dic = vis_dic.get(review_state, state)
59
        if inner_vis_dic is _marker:
60
            state = state
61
        if type(inner_vis_dic) is DictType:
62
            state = inner_vis_dic.get(mode, state)
63
            state = state
64
        elif not inner_vis_dic:
65
            state = 'invisible'
66
        elif inner_vis_dic < 0:
67
            state = 'hidden'
68
69
        return state
70
71
72
class SamplingWorkflowWidgetVisibility(object):
73
    """
74
    This will handle Handling 'DateSampled' and 'SamplingDate' fields'
75
    visibilities based on Sampling Workflow (SWF)status. We must check the
76
    attribute saved on the sample, not the bika_setup value though. See the
77
    internal comments how it enables/disables WidgetVisibility depending on SWF.
78
    """
79
    implements(IATWidgetVisibility)
80
81
    def __init__(self, context):
82
        self.context = context
83
        self.sort = 10
84
85
    def __call__(self, context, mode, field, default):
86
        fields = ['Sampler', 'DateSampled', 'SamplingDate']
87
        state = default if default else 'invisible'
88
        fieldName = field.getName()
89
        if fieldName not in fields:
90
            return state
91
92
        # If object has been already created, get SWF statues from it.
93
        if hasattr(self.context, 'getSamplingWorkflowEnabled') and \
94
                context.getSamplingWorkflowEnabled() is not '':
95
            swf_enabled = context.getSamplingWorkflowEnabled()
96
        else:
97
            swf_enabled = context.bika_setup.getSamplingWorkflowEnabled()
98
99
        # If SWF Enabled, we mostly use the dictionary from the Field, but:
100
        # - DateSampled: invisible during creation.
101
        # - SamplingDate and Sampler: visible and editable until sample due.
102
        if swf_enabled:
103
            if fieldName == 'DateSampled':
104
                if mode == 'add':
105
                    state = 'invisible'
106
                    field.required = 0
107
            elif fieldName in fields:
108
                if mode == 'header_table':
109
                    state = 'prominent'
110
                elif mode == 'view':
111
                    state = 'visible'
112
        # If SamplingWorkflow is Disabled:
113
        #  - DateSampled: visible,
114
        #                 not editable after creation (appears in header_table),
115
        #                 required in 'add' view.
116
        #  - 'SamplingDate' and 'Sampler': disabled everywhere.
117
        else:
118
            if fieldName == 'DateSampled':
119
                if mode == 'add':
120
                    state = 'edit'
121
                    field.required = 1
122
                elif mode == 'edit':
123
                    state = 'invisible'
124
                elif mode == 'view':
125
                    state = 'visible'
126
                elif mode == 'header_table':
127
                    # In the Schema definition, DateSampled is 'prominent' for
128
                    # 'header_table' to let users edit it after receiving
129
                    # the Sample. But if SWF is disabled, DateSampled must be
130
                    # filled during creation and never editable.
131
                    state = 'visible'
132
            elif fieldName in fields:
133
                state = 'invisible'
134
        return state
135
136
137
class ClientFieldWidgetVisibility(object):
138
    """The Client field is editable by default in ar_add.  This adapter
139
    will force the Client field to be hidden when it should not be set
140
    by the user.
141
    """
142
    implements(IATWidgetVisibility)
143
144
    def __init__(self, context):
145
        self.context = context
146
        self.sort = 10
147
148
    def __call__(self, context, mode, field, default):
149
        state = default if default else 'hidden'
150
        fieldName = field.getName()
151
        if fieldName != 'Client':
152
            return state
153
        parent = self.context.aq_parent
154
155
        if IBatch.providedBy(parent):
156
            if parent.getClient():
157
                return 'hidden'
158
159
        if IClient.providedBy(parent):
160
            return 'hidden'
161
162
        return state
163
164
class BatchARAdd_BatchFieldWidgetVisibility(object):
165
    """This will force the 'Batch' field to 'hidden' in ar_add when the parent
166
    context is a Batch.
167
    """
168
    implements(IATWidgetVisibility)
169
170
    def __init__(self, context):
171
        self.context = context
172
        self.sort = 10
173
174
    def __call__(self, context, mode, field, default):
175
        state = default if default else 'visible'
176
        fieldName = field.getName()
177
        if fieldName == 'Batch' and context.aq_parent.portal_type == 'Batch':
178
            return 'hidden'
179
        return state
180
181
class OptionalFieldsWidgetVisibility(object):
182
    """Remove 'hidden attributes' (fields in registry bika.lims.hiddenattributes).
183
       fieldName = field.getName()
184
    """
185
    implements(IATWidgetVisibility)
186
187
    def __init__(self, context):
188
        self.context = context
189
        self.sort = 5
190
191
    def __call__(self, context, mode, field, default):
192
        state = default if default else 'visible'
193
        hiddenattributes = getHiddenAttributesForClass(context.portal_type)
194
        if field.getName() in hiddenattributes:
195
            state = "hidden"
196
        return state
197
198 View Code Duplication
class HideARPriceFields(object):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
199
    """Hide related fields in ARs when ShowPrices is disabled
200
    """
201
    implements(IATWidgetVisibility)
202
203
    def __init__(self, context):
204
        self.context = context
205
        self.sort = 3
206
207
    def __call__(self, context, mode, field, default):
208
        fields = ['InvoiceExclude']
209
        ShowPrices = context.bika_setup.getShowPrices()
210
        state = default if default else 'invisible'
211
        fieldName = field.getName()
212
        if fieldName in fields and not ShowPrices:
213
            state = 'invisible'
214
        return state
215
216 View Code Duplication
class HideClientDiscountFields(object):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
217
    """Hide related fields in ARs when ShowPrices is disabled
218
    """
219
    implements(IATWidgetVisibility)
220
221
    def __init__(self, context):
222
        self.context = context
223
        self.sort = 3
224
225
    def __call__(self, context, mode, field, default):
226
        fields = ['BulkDiscount', 'MemberDiscountApplies']
227
        ShowPrices = context.bika_setup.getShowPrices()
228
        state = default if default else 'invisible'
229
        fieldName = field.getName()
230
        if fieldName in fields and not ShowPrices:
231
            state = 'invisible'
232
        return state
233
234
235
class SampleDateReceived(object):
236
    """DateReceived is editable in sample context, only if all related analyses
237
    are not yet submitted.
238
    """
239
    implements(IATWidgetVisibility)
240
241
    def __init__(self, context):
242
        self.context = context
243
        self.sort = 3
244
245
    def __call__(self, context, mode, field, default):
246
        state = default if default else 'visible'
247
        if field.getName() == 'DateReceived':
248
            # if ONLY "sample_received" analyses exist, it is permitted
249
            # to edit the field.
250
            wf = get_tool('portal_workflow')
251
            anwf = wf['bika_analysis_workflow']
252
            state_ids = [s for s in anwf.states if s != 'sample_received']
253
            if context.getAnalyses(review_state=state_ids):
254
                if mode == 'edit':
255
                    state = 'invisible'
256
                elif mode == 'view':
257
                    state = 'visible'
258
        return state
259