Total Complexity | 64 |
Total Lines | 254 |
Duplicated Lines | 24.8 % |
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.workflow.analysis.guards 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 | |||
8 | from bika.lims import api |
||
9 | from bika.lims.interfaces.analysis import IRequestAnalysis |
||
10 | from bika.lims import workflow as wf |
||
11 | |||
12 | |||
13 | def publish(obj): |
||
14 | """ Returns true if the 'publish' transition can be performed to the |
||
15 | analysis passed in. |
||
16 | In accordance with bika_analysis_workflow, 'publish' |
||
17 | transition can only be performed if the state of the analysis is verified, |
||
18 | so this guard only checks if the analysis state is active: there is no need |
||
19 | of additional checks, cause the DC Workflow machinery will already take |
||
20 | care of them. |
||
21 | :returns: true or false |
||
22 | """ |
||
23 | return wf.isBasicTransitionAllowed(obj) |
||
24 | |||
25 | |||
26 | def attach(obj): |
||
27 | if not wf.isBasicTransitionAllowed(obj): |
||
28 | return False |
||
29 | if not obj.getAttachment(): |
||
30 | return obj.getAttachmentOption() != 'r' |
||
31 | return True |
||
32 | |||
33 | |||
34 | def dependencies_guard(analysis, transition_id): |
||
35 | """Return whether the transition(s) passed in can be performed or not to |
||
36 | the dependencies of the analysis passed in |
||
37 | """ |
||
38 | if isinstance(transition_id, list): |
||
39 | for transition_id in transition_id: |
||
40 | if not dependencies_guard(analysis, transition_id): |
||
41 | return False |
||
42 | return True |
||
43 | |||
44 | for dependency in analysis.getDependencies(): |
||
45 | if not wf.isTransitionAllowed(dependency, transition_id): |
||
46 | if not wf.wasTransitionPerformed(dependency, transition_id): |
||
47 | return False |
||
48 | return True |
||
49 | |||
50 | |||
51 | def guard_assign(analysis): |
||
52 | """Return whether the transition "assign" can be performed or not |
||
53 | """ |
||
54 | # TODO Workflow - Analysis. Assign guard to return True only in WS.Add? |
||
55 | # We need "unassigned" analyses to appear in Worksheet Add analyses. |
||
56 | # Hence, it returns True if the analysis has not been assigned to any |
||
57 | # worksheet yet. The problem is this can end up (if the 'assign' |
||
58 | # transition is displayed in listings other than WS Add Analyses) |
||
59 | # with an analysis transitioned to 'assigned' state, but without |
||
60 | # a worksheet assigned!. This transition should only be triggered by |
||
61 | # content.worksheet.addAnalysis (see that func for more info) |
||
62 | |||
63 | # Cannot assign if the Sample has not been received |
||
64 | if not analysis.isSampleReceived(): |
||
65 | return False |
||
66 | |||
67 | # Check if only LabManager and Manager roles can manage worksheets |
||
68 | if analysis.bika_setup.getRestrictWorksheetManagement(): |
||
69 | member = api.get_current_user() |
||
70 | super_roles = ["LabManager", "Manager"] |
||
71 | if (len(set(super_roles) - set(member.getRoles())) == len(super_roles)): |
||
72 | # Current user is not a "super-user" |
||
73 | return False |
||
74 | |||
75 | # Cannot assign if the analysis has a worksheet assigned already |
||
76 | if analysis.getWorksheet(): |
||
77 | return False |
||
78 | |||
79 | return True |
||
80 | |||
81 | |||
82 | def guard_unassign(analysis): |
||
83 | """Return whether the transition "unassign" can be performed or not |
||
84 | """ |
||
85 | # Check if only LabManager and Manager roles can manage worksheets |
||
86 | if analysis.bika_setup.getRestrictWorksheetManagement(): |
||
87 | member = api.get_current_user() |
||
88 | super_roles = ["LabManager", "Manager"] |
||
89 | if (len(set(super_roles) - set(member.getRoles())) == len(super_roles)): |
||
90 | # Current user is not a "super-user" |
||
91 | return False |
||
92 | |||
93 | # Cannot unassign if the analysis does not have a worksheet assigned |
||
94 | worksheet = analysis.getWorksheet() |
||
95 | if not worksheet: |
||
96 | return False |
||
97 | |||
98 | # Cannot unassign if the worksheet is not open |
||
99 | if api.get_workflow_status_of(worksheet) != "open": |
||
100 | return False |
||
101 | |||
102 | return True |
||
103 | |||
104 | |||
105 | def guard_cancel(analysis): |
||
106 | """Return whether the transition "cancel" can be performed or not. Returns |
||
107 | True only when the Analysis Request the analysis belongs to is in cancelled |
||
108 | state. Otherwise, returns False. |
||
109 | """ |
||
110 | return not api.is_active(analysis.getRequest()) |
||
111 | |||
112 | |||
113 | def guard_reinstate(analysis): |
||
114 | """Return whether the transition "reinstate" can be performed or not. |
||
115 | Returns True only when the Analysis Request the analysis belongs to is in a |
||
116 | non-cancelled state. Otherwise, returns False. |
||
117 | """ |
||
118 | return api.is_active(analysis.getRequest()) |
||
119 | |||
120 | |||
121 | def guard_submit(analysis): |
||
122 | """Return whether the transition "submit" can be performed or not |
||
123 | """ |
||
124 | # Cannot submit without a result |
||
125 | if not analysis.getResult(): |
||
126 | return False |
||
127 | |||
128 | # Cannot submit with interims without value |
||
129 | for interim in analysis.getInterimFields(): |
||
130 | if not interim.get("value", ""): |
||
131 | return False |
||
132 | |||
133 | # Check if can submit based on the Analysis Request state |
||
134 | if IRequestAnalysis.providedBy(analysis): |
||
135 | point_of_capture = analysis.getPointOfCapture() |
||
136 | # Cannot submit if the Sample has not been received |
||
137 | if point_of_capture == "lab" and not analysis.isSampleReceived(): |
||
138 | return False |
||
139 | # Cannot submit if the Sample has not been sampled |
||
140 | if point_of_capture == "field" and not analysis.isSampleSampled(): |
||
141 | return False |
||
142 | |||
143 | # Check if the current user can submit if is not assigned |
||
144 | if not analysis.bika_setup.getAllowToSubmitNotAssigned(): |
||
145 | member = api.get_current_user() |
||
146 | super_roles = ["LabManager", "Manager"] |
||
147 | if (len(set(super_roles) - set(member.getRoles())) == len(super_roles)): |
||
148 | # Cannot submit if unassigned |
||
149 | if not analysis.getAnalyst(): |
||
150 | return False |
||
151 | # Cannot submit if assigned analyst is not the current user |
||
152 | if analysis.getAnalyst() != member.getId(): |
||
153 | return False |
||
154 | |||
155 | # Check dependencies (analyses this analysis depends on) |
||
156 | return dependencies_guard(analysis, "submit") |
||
157 | |||
158 | |||
159 | View Code Duplication | def guard_multi_verify(analysis): |
|
|
|||
160 | """Return whether the transition "multi_verify" can be performed or not |
||
161 | The transition multi_verify will only take place if multi-verification of |
||
162 | results is enabled. |
||
163 | """ |
||
164 | # If there is only one remaining verification, return False |
||
165 | remaining_verifications = analysis.getNumberOfRemainingVerifications() |
||
166 | if remaining_verifications <= 1: |
||
167 | return False |
||
168 | |||
169 | # Check if the current user is the same who submitted the result |
||
170 | user_id = api.get_current_user().getId() |
||
171 | if (analysis.getSubmittedBy() == user_id): |
||
172 | if not analysis.isSelfVerificationEnabled(): |
||
173 | return False |
||
174 | |||
175 | # Check if user is the last verifier and consecutive multi-verification is |
||
176 | # disabled |
||
177 | verifiers = analysis.getVerificators() |
||
178 | mv_type = analysis.bika_setup.getTypeOfmultiVerification() |
||
179 | if verifiers and verifiers[:-1] == user_id \ |
||
180 | and mv_type == "self_multi_not_cons": |
||
181 | return False |
||
182 | |||
183 | # Check if user verified before and self multi-verification is disabled |
||
184 | if user_id in verifiers and mv_type == "self_multi_disabled": |
||
185 | return False |
||
186 | |||
187 | # Check dependencies (analyses this analysis depends on) |
||
188 | return dependencies_guard(analysis, ["verify", "multi_verify"]) |
||
189 | |||
190 | |||
191 | View Code Duplication | def guard_verify(analysis): |
|
192 | """Return whether the transition "verify" can be performed or not |
||
193 | """ |
||
194 | # Check if multi-verification |
||
195 | remaining_verifications = analysis.getNumberOfRemainingVerifications() |
||
196 | if remaining_verifications > 1: |
||
197 | return False |
||
198 | |||
199 | # Check if the current user is the same that submitted the result |
||
200 | user_id = api.get_current_user().getId() |
||
201 | if (analysis.getSubmittedBy() == user_id): |
||
202 | if not analysis.isSelfVerificationEnabled(): |
||
203 | return False |
||
204 | |||
205 | if analysis.getNumberOfRequiredVerifications() <= 1: |
||
206 | # Check dependencies (analyses this analysis depends on) |
||
207 | return dependencies_guard(analysis, "verify") |
||
208 | |||
209 | # This analysis requires more than one verification |
||
210 | # Check if user is the last verifier and consecutive multi-verification is |
||
211 | # disabled |
||
212 | verifiers = analysis.getVerificators() |
||
213 | mv_type = analysis.bika_setup.getTypeOfmultiVerification() |
||
214 | if verifiers and verifiers[:-1] == user_id \ |
||
215 | and mv_type == "self_multi_not_cons": |
||
216 | return False |
||
217 | |||
218 | # Check if user verified before and self multi-verification is disabled |
||
219 | if user_id in verifiers and mv_type == "self_multi_disabled": |
||
220 | return False |
||
221 | |||
222 | # Check dependencies (analyses this analysis depends on) |
||
223 | return dependencies_guard(analysis, ["verify", "multi_verify"]) |
||
224 | |||
225 | |||
226 | def guard_retract(analysis): |
||
227 | """ Return whether the transition "retract" can be performed or not |
||
228 | """ |
||
229 | # Cannot retract if there are dependents that cannot be retracted |
||
230 | for dependent in analysis.getDependents(): |
||
231 | if not wf.isTransitionAllowed(dependent, "retract"): |
||
232 | return False |
||
233 | |||
234 | # Cannot retract if all dependencies have been verified |
||
235 | dependencies = analysis.getDependencies() |
||
236 | if not dependencies: |
||
237 | return True |
||
238 | for dependency in dependencies: |
||
239 | if not wf.wasTransitionPerformed(dependency, "verify"): |
||
240 | return True |
||
241 | |||
242 | return False |
||
243 | |||
244 | |||
245 | def guard_reject(analysis): |
||
246 | """Return whether the transition "reject" can be performed or not |
||
247 | """ |
||
248 | # Cannot reject if there are dependents that cannot be rejected |
||
249 | for dependent in analysis.getDependents(): |
||
250 | if not wf.isTransitionAllowed(dependent, "reject"): |
||
251 | return False |
||
252 | |||
253 | return True |