Passed
Push — master ( af2087...3e7509 )
by Ramon
05:04
created

bika.lims.workflow.analysis.guards   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 254
Duplicated Lines 24.8 %

Importance

Changes 0
Metric Value
wmc 64
eloc 121
dl 63
loc 254
rs 3.28
c 0
b 0
f 0

12 Functions

Rating   Name   Duplication   Size   Complexity  
A publish() 0 11 1
A attach() 0 6 3
B dependencies_guard() 0 15 7
C guard_multi_verify() 30 30 9
B guard_retract() 0 17 6
A guard_unassign() 0 21 5
A guard_reinstate() 0 6 1
D guard_submit() 0 36 13
A guard_cancel() 0 6 1
A guard_assign() 0 29 5
C guard_verify() 33 33 10
A guard_reject() 0 9 3

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.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):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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