Total Complexity | 61 |
Total Lines | 259 |
Duplicated Lines | 13.13 % |
Changes | 0 |
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:
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): |
|
|
|||
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): |
|
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 |